diff options
author | Steve Block <steveblock@google.com> | 2009-10-08 17:19:54 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-10-20 00:41:58 +0100 |
commit | 231d4e3152a9c27a73b6ac7badbe6be673aa3ddf (patch) | |
tree | a6c7e2d6cd7bfa7011cc39abbb436142d7a4a7c8 /JavaScriptCore/runtime | |
parent | e196732677050bd463301566a68a643b6d14b907 (diff) | |
download | external_webkit-231d4e3152a9c27a73b6ac7badbe6be673aa3ddf.zip external_webkit-231d4e3152a9c27a73b6ac7badbe6be673aa3ddf.tar.gz external_webkit-231d4e3152a9c27a73b6ac7badbe6be673aa3ddf.tar.bz2 |
Merge webkit.org at R49305 : Automatic merge by git.
Change-Id: I8968561bc1bfd72b8923b7118d3728579c6dbcc7
Diffstat (limited to 'JavaScriptCore/runtime')
143 files changed, 3425 insertions, 1214 deletions
diff --git a/JavaScriptCore/runtime/ArgList.h b/JavaScriptCore/runtime/ArgList.h index ab501b6..3227770 100644 --- a/JavaScriptCore/runtime/ArgList.h +++ b/JavaScriptCore/runtime/ArgList.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008, 2009 Apple Computer, Inc. + * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,13 +23,14 @@ #define ArgList_h #include "Register.h" - #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> namespace JSC { - + + class MarkStack; + class MarkedArgumentBuffer : public Noncopyable { private: static const unsigned inlineCapacity = 8; diff --git a/JavaScriptCore/runtime/Arguments.cpp b/JavaScriptCore/runtime/Arguments.cpp index ec9c450..86a8f0a 100644 --- a/JavaScriptCore/runtime/Arguments.cpp +++ b/JavaScriptCore/runtime/Arguments.cpp @@ -179,6 +179,31 @@ bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyNa return JSObject::getOwnPropertySlot(exec, propertyName, slot); } +bool Arguments::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(&isArrayIndex); + if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { + if (i < d->numParameters) { + descriptor.setDescriptor(d->registers[d->firstParameterIndex + i].jsValue(), DontEnum); + } else + descriptor.setDescriptor(d->extraArguments[i - d->numParameters].jsValue(), DontEnum); + return true; + } + + if (propertyName == exec->propertyNames().length && LIKELY(!d->overrodeLength)) { + descriptor.setDescriptor(jsNumber(exec, d->numArguments), DontEnum); + return true; + } + + if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) { + descriptor.setDescriptor(d->callee, DontEnum); + return true; + } + + return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + void Arguments::put(ExecState* exec, unsigned i, JSValue value, PutPropertySlot& slot) { if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { diff --git a/JavaScriptCore/runtime/Arguments.h b/JavaScriptCore/runtime/Arguments.h index 79fe720..5be84a2 100644 --- a/JavaScriptCore/runtime/Arguments.h +++ b/JavaScriptCore/runtime/Arguments.h @@ -28,6 +28,8 @@ #include "JSFunction.h" #include "JSGlobalObject.h" #include "Interpreter.h" +#include "ObjectConstructor.h" +#include "PrototypeFunction.h" namespace JSC { @@ -90,6 +92,7 @@ namespace JSC { void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual void put(ExecState*, unsigned propertyName, JSValue, PutPropertySlot&); virtual bool deleteProperty(ExecState*, const Identifier& propertyName); @@ -113,18 +116,17 @@ namespace JSC { ALWAYS_INLINE void Arguments::getArgumentsData(CallFrame* callFrame, JSFunction*& function, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc) { function = callFrame->callee(); - - CodeBlock* codeBlock = &function->body()->generatedBytecode(); - int numParameters = codeBlock->m_numParameters; + + int numParameters = function->jsExecutable()->parameterCount(); argc = callFrame->argumentCount(); if (argc <= numParameters) - argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters + 1; // + 1 to skip "this" + argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters; else - argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters - argc + 1; // + 1 to skip "this" + argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters - argc; argc -= 1; // - 1 to skip "this" - firstParameterIndex = -RegisterFile::CallFrameHeaderSize - numParameters + 1; // + 1 to skip "this" + firstParameterIndex = -RegisterFile::CallFrameHeaderSize - numParameters; } inline Arguments::Arguments(CallFrame* callFrame) @@ -137,7 +139,7 @@ namespace JSC { int numArguments; getArgumentsData(callFrame, callee, firstParameterIndex, argv, numArguments); - d->numParameters = callee->body()->parameterCount(); + d->numParameters = callee->jsExecutable()->parameterCount(); d->firstParameterIndex = firstParameterIndex; d->numArguments = numArguments; @@ -168,7 +170,7 @@ namespace JSC { : JSObject(callFrame->lexicalGlobalObject()->argumentsStructure()) , d(new ArgumentsData) { - ASSERT(!callFrame->callee()->body()->parameterCount()); + ASSERT(!callFrame->callee()->jsExecutable()->parameterCount()); unsigned numArguments = callFrame->argumentCount() - 1; @@ -214,8 +216,8 @@ namespace JSC { { ASSERT(!d()->registerArray); - size_t numParametersMinusThis = d()->functionBody->generatedBytecode().m_numParameters - 1; - size_t numVars = d()->functionBody->generatedBytecode().m_numVars; + size_t numParametersMinusThis = d()->functionExecutable->generatedBytecode().m_numParameters - 1; + size_t numVars = d()->functionExecutable->generatedBytecode().m_numVars; size_t numLocals = numVars + numParametersMinusThis; if (!numLocals) diff --git a/JavaScriptCore/runtime/ArrayConstructor.cpp b/JavaScriptCore/runtime/ArrayConstructor.cpp index e96bdfc..0237fd4 100644 --- a/JavaScriptCore/runtime/ArrayConstructor.cpp +++ b/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -25,15 +25,19 @@ #include "ArrayConstructor.h" #include "ArrayPrototype.h" +#include "Error.h" #include "JSArray.h" #include "JSFunction.h" #include "Lookup.h" +#include "PrototypeFunction.h" namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ArrayConstructor); + +static JSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*, JSObject*, JSValue, const ArgList&); -ArrayConstructor::ArrayConstructor(ExecState* exec, PassRefPtr<Structure> structure, ArrayPrototype* arrayPrototype) +ArrayConstructor::ArrayConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ArrayPrototype* arrayPrototype, Structure* prototypeFunctionStructure) : InternalFunction(&exec->globalData(), structure, Identifier(exec, arrayPrototype->classInfo()->className)) { // ECMA 15.4.3.1 Array.prototype @@ -41,6 +45,9 @@ ArrayConstructor::ArrayConstructor(ExecState* exec, PassRefPtr<Structure> struct // no. of arguments for constructor putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); + + // ES5 + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().isArray, arrayConstructorIsArray), DontEnum); } static JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) @@ -82,4 +89,9 @@ CallType ArrayConstructor::getCallData(CallData& callData) return CallTypeHost; } +JSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*, JSObject*, JSValue, const ArgList& args) +{ + return jsBoolean(args.at(0).inherits(&JSArray::info)); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/ArrayConstructor.h b/JavaScriptCore/runtime/ArrayConstructor.h index 8300d8c..6d25400 100644 --- a/JavaScriptCore/runtime/ArrayConstructor.h +++ b/JavaScriptCore/runtime/ArrayConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class ArrayConstructor : public InternalFunction { public: - ArrayConstructor(ExecState*, PassRefPtr<Structure>, ArrayPrototype*); + ArrayConstructor(ExecState*, NonNullPassRefPtr<Structure>, ArrayPrototype*, Structure*); virtual ConstructType getConstructData(ConstructData&); virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/ArrayPrototype.cpp b/JavaScriptCore/runtime/ArrayPrototype.cpp index 807e59a..7a89447 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -67,7 +67,7 @@ static JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*, JSObject*, JS namespace JSC { -static inline bool isNumericCompareFunction(CallType callType, const CallData& callData) +static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData) { if (callType != CallTypeJS) return false; @@ -75,10 +75,10 @@ static inline bool isNumericCompareFunction(CallType callType, const CallData& c #if ENABLE(JIT) // If the JIT is enabled then we need to preserve the invariant that every // function with a CodeBlock also has JIT code. - callData.js.functionBody->jitCode(callData.js.scopeChain); - CodeBlock& codeBlock = callData.js.functionBody->generatedBytecode(); + callData.js.functionExecutable->jitCode(exec, callData.js.scopeChain); + CodeBlock& codeBlock = callData.js.functionExecutable->generatedBytecode(); #else - CodeBlock& codeBlock = callData.js.functionBody->bytecode(callData.js.scopeChain); + CodeBlock& codeBlock = callData.js.functionExecutable->bytecode(exec, callData.js.scopeChain); #endif return codeBlock.isNumericCompareFunction(); @@ -115,7 +115,7 @@ const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::a */ // ECMA 15.4.4 -ArrayPrototype::ArrayPrototype(PassRefPtr<Structure> structure) +ArrayPrototype::ArrayPrototype(NonNullPassRefPtr<Structure> structure) : JSArray(structure) { } @@ -125,6 +125,11 @@ bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& prope return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, slot); } +bool ArrayPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSArray>(exec, ExecState::arrayTable(exec), this, propertyName, descriptor); +} + // ------------------------------ Array Functions ---------------------------- // Helper function @@ -144,10 +149,11 @@ static void putProperty(ExecState* exec, JSObject* obj, const Identifier& proper JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&JSArray::info)) + bool isRealArray = isJSArray(&exec->globalData(), thisValue); + if (!isRealArray && !thisValue.inherits(&JSArray::info)) return throwError(exec, TypeError); - JSObject* thisObj = asArray(thisValue); - + JSArray* thisObj = asArray(thisValue); + HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) { if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth) @@ -158,39 +164,53 @@ JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue if (alreadyVisited) return jsEmptyString(exec); // return an empty string, avoiding infinite recursion. - Vector<UChar, 256> strBuffer; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned totalSize = length ? length - 1 : 0; + Vector<RefPtr<UString::Rep>, 256> strBuffer(length); for (unsigned k = 0; k < length; k++) { - if (k >= 1) - strBuffer.append(','); - if (!strBuffer.data()) { - JSObject* error = Error::create(exec, GeneralError, "Out of memory"); - exec->setException(error); - break; - } - - JSValue element = thisObj->get(exec, k); + JSValue element; + if (isRealArray && thisObj->canGetIndex(k)) + element = thisObj->getIndex(k); + else + element = thisObj->get(exec, k); + if (element.isUndefinedOrNull()) continue; - + UString str = element.toString(exec); - strBuffer.append(str.data(), str.size()); - + strBuffer[k] = str.rep(); + totalSize += str.size(); + if (!strBuffer.data()) { JSObject* error = Error::create(exec, GeneralError, "Out of memory"); exec->setException(error); } - + if (exec->hadException()) break; } arrayVisitedElements.remove(thisObj); - return jsString(exec, UString(strBuffer.data(), strBuffer.data() ? strBuffer.size() : 0)); + if (!totalSize) + return jsEmptyString(exec); + Vector<UChar> buffer; + buffer.reserveCapacity(totalSize); + if (!buffer.data()) + return throwError(exec, GeneralError, "Out of memory"); + + for (unsigned i = 0; i < length; i++) { + if (i) + buffer.append(','); + if (RefPtr<UString::Rep> rep = strBuffer[i]) + buffer.append(rep->data(), rep->size()); + } + ASSERT(buffer.size() == totalSize); + unsigned finalSize = buffer.size(); + return jsString(exec, UString(buffer.releaseBuffer(), finalSize, false)); } JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&JSArray::info)) + if (!thisValue.inherits(&JSArray::info)) return throwError(exec, TypeError); JSObject* thisObj = asArray(thisValue); @@ -298,7 +318,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec, JSObject*, JSValue t ArgList::const_iterator it = args.begin(); ArgList::const_iterator end = args.end(); while (1) { - if (curArg.isObject(&JSArray::info)) { + if (curArg.inherits(&JSArray::info)) { unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec); JSObject* curObject = curArg.toObject(exec); for (unsigned k = 0; k < length; ++k) { @@ -456,7 +476,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec, JSObject*, JSValue thi CallType callType = function.getCallData(callData); if (thisObj->classInfo() == &JSArray::info) { - if (isNumericCompareFunction(callType, callData)) + if (isNumericCompareFunction(exec, callType, callData)) asArray(thisObj)->sortNumeric(exec, function, callType, callData); else if (callType != CallTypeNone) asArray(thisObj)->sort(exec, function, callType, callData); diff --git a/JavaScriptCore/runtime/ArrayPrototype.h b/JavaScriptCore/runtime/ArrayPrototype.h index 2165089..e52914c 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.h +++ b/JavaScriptCore/runtime/ArrayPrototype.h @@ -28,9 +28,10 @@ namespace JSC { class ArrayPrototype : public JSArray { public: - explicit ArrayPrototype(PassRefPtr<Structure>); + explicit ArrayPrototype(NonNullPassRefPtr<Structure>); bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; diff --git a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h index b9f738f..929a5e7 100644 --- a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h +++ b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -38,7 +38,7 @@ namespace JSC { : m_object(object) { if (!m_object->structure()->isDictionary()) - m_object->setStructure(Structure::toDictionaryTransition(m_object->structure())); + m_object->setStructure(Structure::toCacheableDictionaryTransition(m_object->structure())); } ~BatchedTransitionOptimizer() diff --git a/JavaScriptCore/runtime/BooleanConstructor.cpp b/JavaScriptCore/runtime/BooleanConstructor.cpp index 9fcba37..b0d8df3 100644 --- a/JavaScriptCore/runtime/BooleanConstructor.cpp +++ b/JavaScriptCore/runtime/BooleanConstructor.cpp @@ -28,7 +28,7 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(BooleanConstructor); -BooleanConstructor::BooleanConstructor(ExecState* exec, PassRefPtr<Structure> structure, BooleanPrototype* booleanPrototype) +BooleanConstructor::BooleanConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, BooleanPrototype* booleanPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, booleanPrototype->classInfo()->className)) { putDirectWithoutTransition(exec->propertyNames().prototype, booleanPrototype, DontEnum | DontDelete | ReadOnly); diff --git a/JavaScriptCore/runtime/BooleanConstructor.h b/JavaScriptCore/runtime/BooleanConstructor.h index d9f51ab..1d8a26a 100644 --- a/JavaScriptCore/runtime/BooleanConstructor.h +++ b/JavaScriptCore/runtime/BooleanConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class BooleanConstructor : public InternalFunction { public: - BooleanConstructor(ExecState*, PassRefPtr<Structure>, BooleanPrototype*); + BooleanConstructor(ExecState*, NonNullPassRefPtr<Structure>, BooleanPrototype*); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/BooleanObject.cpp b/JavaScriptCore/runtime/BooleanObject.cpp index 01f695a..c9b3846 100644 --- a/JavaScriptCore/runtime/BooleanObject.cpp +++ b/JavaScriptCore/runtime/BooleanObject.cpp @@ -27,7 +27,7 @@ ASSERT_CLASS_FITS_IN_CELL(BooleanObject); const ClassInfo BooleanObject::info = { "Boolean", 0, 0, 0 }; -BooleanObject::BooleanObject(PassRefPtr<Structure> structure) +BooleanObject::BooleanObject(NonNullPassRefPtr<Structure> structure) : JSWrapperObject(structure) { } diff --git a/JavaScriptCore/runtime/BooleanObject.h b/JavaScriptCore/runtime/BooleanObject.h index cfd55fe..28f796a 100644 --- a/JavaScriptCore/runtime/BooleanObject.h +++ b/JavaScriptCore/runtime/BooleanObject.h @@ -27,10 +27,15 @@ namespace JSC { class BooleanObject : public JSWrapperObject { public: - explicit BooleanObject(PassRefPtr<Structure>); + explicit BooleanObject(NonNullPassRefPtr<Structure>); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark | HasDefaultGetPropertyNames)); + } }; BooleanObject* asBooleanObject(JSValue); diff --git a/JavaScriptCore/runtime/BooleanPrototype.cpp b/JavaScriptCore/runtime/BooleanPrototype.cpp index 703a568..8d338f9 100644 --- a/JavaScriptCore/runtime/BooleanPrototype.cpp +++ b/JavaScriptCore/runtime/BooleanPrototype.cpp @@ -37,7 +37,7 @@ static JSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState*, JSObject*, JSVa // ECMA 15.6.4 -BooleanPrototype::BooleanPrototype(ExecState* exec, PassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) +BooleanPrototype::BooleanPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) : BooleanObject(structure) { setInternalValue(jsBoolean(false)); @@ -59,7 +59,7 @@ JSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState* exec, JSObject*, JSVal if (thisValue == jsBoolean(true)) return jsNontrivialString(exec, "true"); - if (!thisValue.isObject(&BooleanObject::info)) + if (!thisValue.inherits(&BooleanObject::info)) return throwError(exec, TypeError); if (asBooleanObject(thisValue)->internalValue() == jsBoolean(false)) @@ -74,7 +74,7 @@ JSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState* exec, JSObject*, JSValu if (thisValue.isBoolean()) return thisValue; - if (!thisValue.isObject(&BooleanObject::info)) + if (!thisValue.inherits(&BooleanObject::info)) return throwError(exec, TypeError); return asBooleanObject(thisValue)->internalValue(); diff --git a/JavaScriptCore/runtime/BooleanPrototype.h b/JavaScriptCore/runtime/BooleanPrototype.h index 16f80b5..cc69b3f 100644 --- a/JavaScriptCore/runtime/BooleanPrototype.h +++ b/JavaScriptCore/runtime/BooleanPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class BooleanPrototype : public BooleanObject { public: - BooleanPrototype(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure); + BooleanPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/CallData.h b/JavaScriptCore/runtime/CallData.h index d5b0172..24c19f9 100644 --- a/JavaScriptCore/runtime/CallData.h +++ b/JavaScriptCore/runtime/CallData.h @@ -35,7 +35,7 @@ namespace JSC { class ArgList; class ExecState; - class FunctionBodyNode; + class FunctionExecutable; class JSObject; class JSValue; class ScopeChainNode; @@ -53,7 +53,7 @@ namespace JSC { NativeFunction function; } native; struct { - FunctionBodyNode* functionBody; + FunctionExecutable* functionExecutable; ScopeChainNode* scopeChain; } js; }; diff --git a/JavaScriptCore/runtime/Collector.cpp b/JavaScriptCore/runtime/Collector.cpp index c188016..1a5eb89 100644 --- a/JavaScriptCore/runtime/Collector.cpp +++ b/JavaScriptCore/runtime/Collector.cpp @@ -23,8 +23,10 @@ #include "ArgList.h" #include "CallFrame.h" +#include "CodeBlock.h" #include "CollectorHeapIterator.h" #include "Interpreter.h" +#include "JSArray.h" #include "JSGlobalObject.h" #include "JSLock.h" #include "JSONObject.h" @@ -58,11 +60,18 @@ #elif PLATFORM(WIN_OS) #include <windows.h> +#include <malloc.h> + +#elif PLATFORM(HAIKU) + +#include <OS.h> #elif PLATFORM(UNIX) #include <stdlib.h> +#if !PLATFORM(HAIKU) #include <sys/mman.h> +#endif #include <unistd.h> #if PLATFORM(SOLARIS) @@ -75,9 +84,15 @@ #include <pthread_np.h> #endif +#if PLATFORM(QNX) +#include <fcntl.h> +#include <sys/procfs.h> +#include <stdio.h> +#include <errno.h> +#endif + #endif -#define DEBUG_COLLECTOR 0 #define COLLECT_ON_EVERY_ALLOCATION 0 using std::max; @@ -86,7 +101,6 @@ namespace JSC { // tunable parameters -const size_t SPARE_EMPTY_BLOCKS = 2; const size_t GROWTH_FACTOR = 2; const size_t LOW_WATER_FACTOR = 4; const size_t ALLOCATIONS_PER_COLLECTION = 4000; @@ -99,8 +113,6 @@ const size_t MAX_NUM_BLOCKS = 256; // Max size of collector heap set to 16 MB static RHeap* userChunk = 0; #endif -static void freeHeap(CollectorHeap*); - #if ENABLE(JSC_MULTIPLE_THREADS) #if PLATFORM(DARWIN) @@ -189,8 +201,8 @@ void Heap::destroy() ASSERT(!primaryHeap.numLiveObjects); - freeHeap(&primaryHeap); - freeHeap(&numberHeap); + freeBlocks(&primaryHeap); + freeBlocks(&numberHeap); #if ENABLE(JSC_MULTIPLE_THREADS) if (m_currentThreadRegistrar) { @@ -210,7 +222,7 @@ void Heap::destroy() } template <HeapType heapType> -static NEVER_INLINE CollectorBlock* allocateBlock() +NEVER_INLINE CollectorBlock* Heap::allocateBlock() { #if PLATFORM(DARWIN) vm_address_t address = 0; @@ -224,9 +236,15 @@ static NEVER_INLINE CollectorBlock* allocateBlock() uintptr_t address = reinterpret_cast<uintptr_t>(mask); memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE); +#elif PLATFORM(WINCE) + void* address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #elif PLATFORM(WIN_OS) - // windows virtual address granularity is naturally 64k - LPVOID address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#if COMPILER(MINGW) + void* address = __mingw_aligned_malloc(BLOCK_SIZE, BLOCK_SIZE); +#else + void* address = _aligned_malloc(BLOCK_SIZE, BLOCK_SIZE); +#endif + memset(address, 0, BLOCK_SIZE); #elif HAVE(POSIX_MEMALIGN) void* address; posix_memalign(&address, BLOCK_SIZE, BLOCK_SIZE); @@ -258,18 +276,58 @@ static NEVER_INLINE CollectorBlock* allocateBlock() address += adjust; memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE); #endif - reinterpret_cast<CollectorBlock*>(address)->type = heapType; - return reinterpret_cast<CollectorBlock*>(address); + + CollectorBlock* block = reinterpret_cast<CollectorBlock*>(address); + block->freeList = block->cells; + block->heap = this; + block->type = heapType; + + CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap; + size_t numBlocks = heap.numBlocks; + if (heap.usedBlocks == numBlocks) { + static const size_t maxNumBlocks = ULONG_MAX / sizeof(CollectorBlock*) / GROWTH_FACTOR; + if (numBlocks > maxNumBlocks) + CRASH(); + numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); + heap.numBlocks = numBlocks; + heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, numBlocks * sizeof(CollectorBlock*))); + } + heap.blocks[heap.usedBlocks++] = block; + + return block; +} + +template <HeapType heapType> +NEVER_INLINE void Heap::freeBlock(size_t block) +{ + CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap; + + freeBlock(heap.blocks[block]); + + // swap with the last block so we compact as we go + heap.blocks[block] = heap.blocks[heap.usedBlocks - 1]; + heap.usedBlocks--; + + if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) { + heap.numBlocks = heap.numBlocks / GROWTH_FACTOR; + heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock*))); + } } -static void freeBlock(CollectorBlock* block) +NEVER_INLINE void Heap::freeBlock(CollectorBlock* block) { #if PLATFORM(DARWIN) vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE); #elif PLATFORM(SYMBIAN) userChunk->Free(reinterpret_cast<TAny*>(block)); -#elif PLATFORM(WIN_OS) +#elif PLATFORM(WINCE) VirtualFree(block, 0, MEM_RELEASE); +#elif PLATFORM(WIN_OS) +#if COMPILER(MINGW) + __mingw_aligned_free(block); +#else + _aligned_free(block); +#endif #elif HAVE(POSIX_MEMALIGN) free(block); #else @@ -277,7 +335,7 @@ static void freeBlock(CollectorBlock* block) #endif } -static void freeHeap(CollectorHeap* heap) +void Heap::freeBlocks(CollectorHeap* heap) { for (size_t i = 0; i < heap->usedBlocks; ++i) if (heap->blocks[i]) @@ -370,38 +428,23 @@ collect: #ifndef NDEBUG heap.operationInProgress = NoOperation; #endif - bool collected = collect(); + bool foundGarbage = collect(); + numLiveObjects = heap.numLiveObjects; + usedBlocks = heap.usedBlocks; + i = heap.firstBlockWithPossibleSpace; #ifndef NDEBUG heap.operationInProgress = Allocation; #endif - if (collected) { - numLiveObjects = heap.numLiveObjects; - usedBlocks = heap.usedBlocks; - i = heap.firstBlockWithPossibleSpace; + if (foundGarbage) goto scan; - } - } - - // didn't find a block, and GC didn't reclaim anything, need to allocate a new block - size_t numBlocks = heap.numBlocks; - if (usedBlocks == numBlocks) { - static const size_t maxNumBlocks = ULONG_MAX / sizeof(CollectorBlock*) / GROWTH_FACTOR; - if (numBlocks > maxNumBlocks) - CRASH(); - numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); - heap.numBlocks = numBlocks; - heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, numBlocks * sizeof(CollectorBlock*))); } + // didn't find a block, and GC didn't reclaim anything, need to allocate a new block targetBlock = reinterpret_cast<Block*>(allocateBlock<heapType>()); - targetBlock->freeList = targetBlock->cells; - targetBlock->heap = this; + heap.firstBlockWithPossibleSpace = heap.usedBlocks - 1; targetBlockUsedCells = 0; - heap.blocks[usedBlocks] = reinterpret_cast<CollectorBlock*>(targetBlock); - heap.usedBlocks = usedBlocks + 1; - heap.firstBlockWithPossibleSpace = usedBlocks; } - + // find a free spot in the block and detach it from the free list Cell* newCell = targetBlock->freeList; @@ -486,6 +529,33 @@ static void* getStackBase(void* previousFrame) } #endif +#if PLATFORM(QNX) +static inline void *currentThreadStackBaseQNX() +{ + static void* stackBase = 0; + static size_t stackSize = 0; + static pthread_t stackThread; + pthread_t thread = pthread_self(); + if (stackBase == 0 || thread != stackThread) { + struct _debug_thread_info threadInfo; + memset(&threadInfo, 0, sizeof(threadInfo)); + threadInfo.tid = pthread_self(); + int fd = open("/proc/self", O_RDONLY); + if (fd == -1) { + LOG_ERROR("Unable to open /proc/self (errno: %d)", errno); + return 0; + } + devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0); + close(fd); + stackBase = reinterpret_cast<void*>(threadInfo.stkbase); + stackSize = threadInfo.stksize; + ASSERT(stackBase); + stackThread = thread; + } + return static_cast<char*>(stackBase) + stackSize; +} +#endif + static inline void* currentThreadStackBase() { #if PLATFORM(DARWIN) @@ -511,6 +581,8 @@ static inline void* currentThreadStackBase() : "=r" (pTib) ); return static_cast<void*>(pTib->StackBase); +#elif PLATFORM(QNX) + return currentThreadStackBaseQNX(); #elif PLATFORM(SOLARIS) stack_t s; thr_stksegment(&s); @@ -529,6 +601,10 @@ static inline void* currentThreadStackBase() stackBase = (void*)info.iBase; } return (void*)stackBase; +#elif PLATFORM(HAIKU) + thread_info threadInfo; + get_thread_info(find_thread(NULL), &threadInfo); + return threadInfo.stack_end; #elif PLATFORM(UNIX) static void* stackBase = 0; static size_t stackSize = 0; @@ -587,6 +663,8 @@ void Heap::makeUsableFromMultipleThreads() void Heap::registerThread() { + ASSERT(!m_globalData->mainThreadOnly || isMainThread()); + if (!m_currentThreadRegistrar || pthread_getspecific(m_currentThreadRegistrar)) return; @@ -683,7 +761,7 @@ void Heap::markConservatively(MarkStack& markStack, void* start, void* end) // Mark the primary heap for (size_t block = 0; block < usedPrimaryBlocks; block++) { if ((primaryBlocks[block] == blockAddr) & (offset <= lastCellOffset)) { - if (reinterpret_cast<CollectorCell*>(xAsBits)->u.freeCell.zeroIfFree != 0) { + if (reinterpret_cast<CollectorCell*>(xAsBits)->u.freeCell.zeroIfFree) { markStack.append(reinterpret_cast<JSCell*>(xAsBits)); markStack.drain(); } @@ -704,10 +782,16 @@ void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markS markConservatively(markStack, stackPointer, stackBase); } +#if COMPILER(GCC) +#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) +#else +#define REGISTER_BUFFER_ALIGNMENT +#endif + void Heap::markCurrentThreadConservatively(MarkStack& markStack) { // setjmp forces volatile registers onto the stack - jmp_buf registers; + jmp_buf registers REGISTER_BUFFER_ALIGNMENT; #if COMPILER(MSVC) #pragma warning(push) #pragma warning(disable: 4611) @@ -896,16 +980,6 @@ void Heap::markStackObjectsConservatively(MarkStack& markStack) #endif } -void Heap::setGCProtectNeedsLocking() -{ - // Most clients do not need to call this, with the notable exception of WebCore. - // Clients that use shared heap have JSLock protection, while others are supposed - // to do explicit locking. WebCore violates this contract in Database code, - // which calls gcUnprotect from a secondary thread. - if (!m_protectedValuesMutex) - m_protectedValuesMutex.set(new Mutex); -} - void Heap::protect(JSValue k) { ASSERT(k); @@ -914,13 +988,7 @@ void Heap::protect(JSValue k) if (!k.isCell()) return; - if (m_protectedValuesMutex) - m_protectedValuesMutex->lock(); - m_protectedValues.add(k.asCell()); - - if (m_protectedValuesMutex) - m_protectedValuesMutex->unlock(); } void Heap::unprotect(JSValue k) @@ -931,38 +999,16 @@ void Heap::unprotect(JSValue k) if (!k.isCell()) return; - if (m_protectedValuesMutex) - m_protectedValuesMutex->lock(); - m_protectedValues.remove(k.asCell()); - - if (m_protectedValuesMutex) - m_protectedValuesMutex->unlock(); -} - -Heap* Heap::heap(JSValue v) -{ - if (!v.isCell()) - return 0; - return Heap::cellBlock(v.asCell())->heap; } void Heap::markProtectedObjects(MarkStack& markStack) { - if (m_protectedValuesMutex) - m_protectedValuesMutex->lock(); - ProtectCountSet::iterator end = m_protectedValues.end(); for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { - JSCell* val = it->first; - if (!val->marked()) { - markStack.append(val); - markStack.drain(); - } + markStack.append(it->first); + markStack.drain(); } - - if (m_protectedValuesMutex) - m_protectedValuesMutex->unlock(); } template <HeapType heapType> size_t Heap::sweep() @@ -1036,31 +1082,34 @@ template <HeapType heapType> size_t Heap::sweep() curBlock->freeList = freeList; curBlock->marked.clearAll(); - if (usedCells == 0) { - emptyBlocks++; - if (emptyBlocks > SPARE_EMPTY_BLOCKS) { -#if !DEBUG_COLLECTOR - freeBlock(reinterpret_cast<CollectorBlock*>(curBlock)); -#endif - // swap with the last block so we compact as we go - heap.blocks[block] = heap.blocks[heap.usedBlocks - 1]; - heap.usedBlocks--; - block--; // Don't move forward a step in this case - - if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) { - heap.numBlocks = heap.numBlocks / GROWTH_FACTOR; - heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock*))); - } - } - } + if (!usedCells) + ++emptyBlocks; } if (heap.numLiveObjects != numLiveObjects) heap.firstBlockWithPossibleSpace = 0; - + heap.numLiveObjects = numLiveObjects; heap.numLiveObjectsAtLastCollect = numLiveObjects; heap.extraCost = 0; + + if (!emptyBlocks) + return numLiveObjects; + + size_t neededCells = 1.25f * (numLiveObjects + max(ALLOCATIONS_PER_COLLECTION, numLiveObjects)); + size_t neededBlocks = (neededCells + HeapConstants<heapType>::cellsPerBlock - 1) / HeapConstants<heapType>::cellsPerBlock; + for (size_t block = 0; block < heap.usedBlocks; block++) { + if (heap.usedBlocks <= neededBlocks) + break; + + Block* curBlock = reinterpret_cast<Block*>(heap.blocks[block]); + if (curBlock->usedCells) + continue; + + freeBlock<heapType>(block); + block--; // Don't move forward a step in this case + } + return numLiveObjects; } @@ -1087,12 +1136,12 @@ bool Heap::collect() markProtectedObjects(markStack); if (m_markListSet && m_markListSet->size()) MarkedArgumentBuffer::markLists(markStack, *m_markListSet); - if (m_globalData->exception && !m_globalData->exception.marked()) + if (m_globalData->exception) markStack.append(m_globalData->exception); m_globalData->interpreter->registerFile().markCallFrames(markStack, this); - m_globalData->smallStrings.mark(); - if (m_globalData->scopeNodeBeingReparsed) - m_globalData->scopeNodeBeingReparsed->markAggregate(markStack); + m_globalData->smallStrings.markChildren(markStack); + if (m_globalData->functionCodeBlockBeingReparsed) + m_globalData->functionCodeBlockBeingReparsed->markAggregate(markStack); if (m_globalData->firstStringifierToMark) JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); @@ -1151,9 +1200,6 @@ size_t Heap::globalObjectCount() size_t Heap::protectedGlobalObjectCount() { - if (m_protectedValuesMutex) - m_protectedValuesMutex->lock(); - size_t count = 0; if (JSGlobalObject* head = m_globalData->head) { JSGlobalObject* o = head; @@ -1164,23 +1210,12 @@ size_t Heap::protectedGlobalObjectCount() } while (o != head); } - if (m_protectedValuesMutex) - m_protectedValuesMutex->unlock(); - return count; } size_t Heap::protectedObjectCount() { - if (m_protectedValuesMutex) - m_protectedValuesMutex->lock(); - - size_t result = m_protectedValues.size(); - - if (m_protectedValuesMutex) - m_protectedValuesMutex->unlock(); - - return result; + return m_protectedValues.size(); } static const char* typeName(JSCell* cell) @@ -1194,7 +1229,7 @@ static const char* typeName(JSCell* cell) if (cell->isGetterSetter()) return "gettersetter"; ASSERT(cell->isObject()); - const ClassInfo* info = static_cast<JSObject*>(cell)->classInfo(); + const ClassInfo* info = cell->classInfo(); return info ? info->className : "Object"; } @@ -1202,16 +1237,10 @@ HashCountedSet<const char*>* Heap::protectedObjectTypeCounts() { HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; - if (m_protectedValuesMutex) - m_protectedValuesMutex->lock(); - ProtectCountSet::iterator end = m_protectedValues.end(); for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) counts->add(typeName(it->first)); - if (m_protectedValuesMutex) - m_protectedValuesMutex->unlock(); - return counts; } diff --git a/JavaScriptCore/runtime/Collector.h b/JavaScriptCore/runtime/Collector.h index 877f890..2ce13dc 100644 --- a/JavaScriptCore/runtime/Collector.h +++ b/JavaScriptCore/runtime/Collector.h @@ -96,11 +96,11 @@ namespace JSC { }; Statistics statistics() const; - void setGCProtectNeedsLocking(); void protect(JSValue); void unprotect(JSValue); static Heap* heap(JSValue); // 0 for immediate values + static Heap* heap(JSCell*); size_t globalObjectCount(); size_t protectedObjectCount(); @@ -133,6 +133,11 @@ namespace JSC { Heap(JSGlobalData*); ~Heap(); + template <HeapType heapType> NEVER_INLINE CollectorBlock* allocateBlock(); + template <HeapType heapType> NEVER_INLINE void freeBlock(size_t); + NEVER_INLINE void freeBlock(CollectorBlock*); + void freeBlocks(CollectorHeap*); + void recordExtraCost(size_t); void markProtectedObjects(MarkStack&); void markCurrentThreadConservatively(MarkStack&); @@ -145,7 +150,6 @@ namespace JSC { CollectorHeap primaryHeap; CollectorHeap numberHeap; - OwnPtr<Mutex> m_protectedValuesMutex; // Only non-null if the client explicitly requested it via setGCPrtotectNeedsLocking(). ProtectCountSet m_protectedValues; HashSet<MarkedArgumentBuffer*>* m_markListSet; @@ -175,7 +179,11 @@ namespace JSC { #endif template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; - const size_t BLOCK_SIZE = 16 * 4096; // 64k +#if PLATFORM(WINCE) || PLATFORM(SYMBIAN) + const size_t BLOCK_SIZE = 64 * 1024; // 64k +#else + const size_t BLOCK_SIZE = 64 * 4096; // 256k +#endif // derived constants const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; diff --git a/JavaScriptCore/runtime/CommonIdentifiers.h b/JavaScriptCore/runtime/CommonIdentifiers.h index 148d3dd..abe5038 100644 --- a/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/JavaScriptCore/runtime/CommonIdentifiers.h @@ -37,17 +37,26 @@ macro(callee) \ macro(caller) \ macro(compile) \ + macro(configurable) \ macro(constructor) \ + macro(create) \ + macro(defineProperty) \ + macro(defineProperties) \ + macro(enumerable) \ macro(eval) \ macro(exec) \ macro(fromCharCode) \ macro(global) \ + macro(get) \ macro(getPrototypeOf) \ + macro(getOwnPropertyDescriptor) \ macro(hasOwnProperty) \ macro(ignoreCase) \ macro(index) \ macro(input) \ + macro(isArray) \ macro(isPrototypeOf) \ + macro(keys) \ macro(length) \ macro(message) \ macro(multiline) \ @@ -56,6 +65,7 @@ macro(parse) \ macro(propertyIsEnumerable) \ macro(prototype) \ + macro(set) \ macro(source) \ macro(test) \ macro(toExponential) \ @@ -66,7 +76,9 @@ macro(toPrecision) \ macro(toString) \ macro(UTC) \ + macro(value) \ macro(valueOf) \ + macro(writable) \ macro(displayName) namespace JSC { diff --git a/JavaScriptCore/runtime/Completion.cpp b/JavaScriptCore/runtime/Completion.cpp index b8b1581..2507698 100644 --- a/JavaScriptCore/runtime/Completion.cpp +++ b/JavaScriptCore/runtime/Completion.cpp @@ -31,40 +31,33 @@ #include "Debugger.h" #include <stdio.h> -#if !PLATFORM(WIN_OS) -#include <unistd.h> -#endif - namespace JSC { Completion checkSyntax(ExecState* exec, const SourceCode& source) { JSLock lock(exec); - int errLine; - UString errMsg; + RefPtr<ProgramExecutable> program = ProgramExecutable::create(exec, source); + JSObject* error = program->checkSyntax(exec); + if (error) + return Completion(Throw, error); - RefPtr<ProgramNode> progNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); - if (!progNode) - return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url())); return Completion(Normal); } Completion evaluate(ExecState* exec, ScopeChain& scopeChain, const SourceCode& source, JSValue thisValue) { JSLock lock(exec); - - int errLine; - UString errMsg; - RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); - if (!programNode) - return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url())); + RefPtr<ProgramExecutable> program = ProgramExecutable::create(exec, source); + JSObject* error = program->compile(exec, scopeChain.node()); + if (error) + return Completion(Throw, error); JSObject* thisObj = (!thisValue || thisValue.isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue.toObject(exec); JSValue exception; - JSValue result = exec->interpreter()->execute(programNode.get(), exec, scopeChain.node(), thisObj, &exception); + JSValue result = exec->interpreter()->execute(program.get(), exec, scopeChain.node(), thisObj, &exception); if (exception) { if (exception.isObject() && asObject(exception)->isWatchdogException()) diff --git a/JavaScriptCore/runtime/ConstructData.h b/JavaScriptCore/runtime/ConstructData.h index 9d580d5..6b954a6 100644 --- a/JavaScriptCore/runtime/ConstructData.h +++ b/JavaScriptCore/runtime/ConstructData.h @@ -33,7 +33,7 @@ namespace JSC { class ArgList; class ExecState; - class FunctionBodyNode; + class FunctionExecutable; class JSObject; class JSValue; class ScopeChainNode; @@ -51,7 +51,7 @@ namespace JSC { NativeConstructor function; } native; struct { - FunctionBodyNode* functionBody; + FunctionExecutable* functionExecutable; ScopeChainNode* scopeChain; } js; }; diff --git a/JavaScriptCore/runtime/DateConstructor.cpp b/JavaScriptCore/runtime/DateConstructor.cpp index 2f52cff..f9b7d84 100644 --- a/JavaScriptCore/runtime/DateConstructor.cpp +++ b/JavaScriptCore/runtime/DateConstructor.cpp @@ -57,7 +57,7 @@ static JSValue JSC_HOST_CALL dateParse(ExecState*, JSObject*, JSValue, const Arg static JSValue JSC_HOST_CALL dateNow(ExecState*, JSObject*, JSValue, const ArgList&); static JSValue JSC_HOST_CALL dateUTC(ExecState*, JSObject*, JSValue, const ArgList&); -DateConstructor::DateConstructor(ExecState* exec, PassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, DatePrototype* datePrototype) +DateConstructor::DateConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, DatePrototype* datePrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, datePrototype->classInfo()->className)) { putDirectWithoutTransition(exec->propertyNames().prototype, datePrototype, DontEnum|DontDelete|ReadOnly); @@ -79,7 +79,7 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) if (numArgs == 0) // new Date() ECMA 15.9.3.3 value = getCurrentUTCTime(); else if (numArgs == 1) { - if (args.at(0).isObject(&DateInstance::info)) + if (args.at(0).inherits(&DateInstance::info)) value = asDateInstance(args.at(0))->internalNumber(); else { JSValue primitive = args.at(0).toPrimitive(exec); diff --git a/JavaScriptCore/runtime/DateConstructor.h b/JavaScriptCore/runtime/DateConstructor.h index dcef3cc..10e450e 100644 --- a/JavaScriptCore/runtime/DateConstructor.h +++ b/JavaScriptCore/runtime/DateConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class DateConstructor : public InternalFunction { public: - DateConstructor(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure, DatePrototype*); + DateConstructor(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, DatePrototype*); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/DateInstance.cpp b/JavaScriptCore/runtime/DateInstance.cpp index 62791ae..4cd58f5 100644 --- a/JavaScriptCore/runtime/DateInstance.cpp +++ b/JavaScriptCore/runtime/DateInstance.cpp @@ -22,6 +22,8 @@ #include "config.h" #include "DateInstance.h" +#include "JSGlobalObject.h" + #include <math.h> #include <wtf/DateMath.h> #include <wtf/MathExtras.h> @@ -39,12 +41,19 @@ struct DateInstance::Cache { const ClassInfo DateInstance::info = {"Date", 0, 0, 0}; -DateInstance::DateInstance(PassRefPtr<Structure> structure) +DateInstance::DateInstance(NonNullPassRefPtr<Structure> structure) : JSWrapperObject(structure) , m_cache(0) { } +DateInstance::DateInstance(ExecState* exec, double time) + : JSWrapperObject(exec->lexicalGlobalObject()->dateStructure()) + , m_cache(0) +{ + setInternalValue(jsNumber(exec, timeClip(time))); +} + DateInstance::~DateInstance() { delete m_cache; diff --git a/JavaScriptCore/runtime/DateInstance.h b/JavaScriptCore/runtime/DateInstance.h index 3b73521..36d90b1 100644 --- a/JavaScriptCore/runtime/DateInstance.h +++ b/JavaScriptCore/runtime/DateInstance.h @@ -31,7 +31,8 @@ namespace JSC { class DateInstance : public JSWrapperObject { public: - explicit DateInstance(PassRefPtr<Structure>); + DateInstance(ExecState*, double); + explicit DateInstance(NonNullPassRefPtr<Structure>); virtual ~DateInstance(); double internalNumber() const { return internalValue().uncheckedGetNumber(); } @@ -41,7 +42,7 @@ namespace JSC { bool getTime(double& milliseconds, int& offset) const; bool getUTCTime(double& milliseconds) const; - static const ClassInfo info; + static JS_EXPORTDATA const ClassInfo info; void msToGregorianDateTime(double, bool outputIsUTC, WTF::GregorianDateTime&) const; diff --git a/JavaScriptCore/runtime/DatePrototype.cpp b/JavaScriptCore/runtime/DatePrototype.cpp index e2482f4..e46ab67 100644 --- a/JavaScriptCore/runtime/DatePrototype.cpp +++ b/JavaScriptCore/runtime/DatePrototype.cpp @@ -198,8 +198,8 @@ static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, L { #if HAVE(LANGINFO_H) static const nl_item formats[] = { D_T_FMT, D_FMT, T_FMT }; -#elif PLATFORM(WINCE) && !PLATFORM(QT) - // strftime() we are using does not support # +#elif (PLATFORM(WINCE) && !PLATFORM(QT)) || PLATFORM(SYMBIAN) + // strftime() does not support '#' on WinCE or Symbian static const char* const formatStrings[] = { "%c", "%x", "%X" }; #else static const char* const formatStrings[] = { "%#c", "%#x", "%X" }; @@ -395,7 +395,7 @@ const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, 0, ExecState // ECMA 15.9.4 -DatePrototype::DatePrototype(ExecState* exec, PassRefPtr<Structure> structure) +DatePrototype::DatePrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure) : DateInstance(structure) { setInternalValue(jsNaN(exec)); @@ -407,11 +407,17 @@ bool DatePrototype::getOwnPropertySlot(ExecState* exec, const Identifier& proper return getStaticFunctionSlot<JSObject>(exec, ExecState::dateTable(exec), this, propertyName, slot); } + +bool DatePrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::dateTable(exec), this, propertyName, descriptor); +} + // Functions JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -428,7 +434,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec, JSObject*, JSValue JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -445,7 +451,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -457,17 +463,17 @@ JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSVal GregorianDateTime t; thisDateObj->msToGregorianDateTime(milli, utc, t); - // Maximum amount of space we need in buffer: 6 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) - // 6 for formatting and one for null termination = 23. We add one extra character to allow us to force null termination. - char buffer[24]; - snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02dZ", 1900 + t.year, t.month + 1, t.monthDay, t.hour, t.minute, t.second); + // Maximum amount of space we need in buffer: 6 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) + // 6 for formatting and one for null termination = 27. We add one extra character to allow us to force null termination. + char buffer[28]; + snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + t.year, t.month + 1, t.monthDay, t.hour, t.minute, t.second, static_cast<int>(fmod(milli, 1000))); buffer[sizeof(buffer) - 1] = 0; return jsNontrivialString(exec, buffer); } JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -484,7 +490,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec, JSObject*, JSVa JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -501,7 +507,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSVa JSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -514,7 +520,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec, JSObject*, JS JSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -527,7 +533,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState* exec, JSObject* JSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -540,7 +546,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState* exec, JSObject* JSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -553,7 +559,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec, JSObject*, JSValue t JSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -570,7 +576,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -587,7 +593,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JS JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -604,7 +610,7 @@ JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -621,7 +627,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -638,7 +644,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -655,7 +661,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue t JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -672,7 +678,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValu JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -689,7 +695,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue th JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -706,7 +712,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -723,7 +729,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -740,7 +746,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -757,7 +763,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValu JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -774,7 +780,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSV JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -791,7 +797,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValu JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = true; @@ -808,7 +814,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSV JSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -823,7 +829,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject*, J JSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -838,7 +844,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState* exec, JSObject* JSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -855,7 +861,7 @@ JSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject*, JSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -868,7 +874,7 @@ JSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec, JSObject*, JSValue t static JSValue setNewValueFromTimeArgs(ExecState* exec, JSValue thisValue, const ArgList& args, int numArgsToUse, bool inputIsUTC) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -899,7 +905,7 @@ static JSValue setNewValueFromTimeArgs(ExecState* exec, JSValue thisValue, const static JSValue setNewValueFromDateArgs(ExecState* exec, JSValue thisValue, const ArgList& args, int numArgsToUse, bool inputIsUTC) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); DateInstance* thisDateObj = asDateInstance(thisValue); @@ -1020,7 +1026,7 @@ JSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState* exec, JSObject*, JS JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; @@ -1062,7 +1068,7 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t JSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&DateInstance::info)) + if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); const bool utc = false; diff --git a/JavaScriptCore/runtime/DatePrototype.h b/JavaScriptCore/runtime/DatePrototype.h index 5f4d0ec..caed2d4 100644 --- a/JavaScriptCore/runtime/DatePrototype.h +++ b/JavaScriptCore/runtime/DatePrototype.h @@ -29,16 +29,17 @@ namespace JSC { class DatePrototype : public DateInstance { public: - DatePrototype(ExecState*, PassRefPtr<Structure>); + DatePrototype(ExecState*, NonNullPassRefPtr<Structure>); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; static PassRefPtr<Structure> createStructure(JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType)); + return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultGetPropertyNames)); } }; diff --git a/JavaScriptCore/runtime/Error.cpp b/JavaScriptCore/runtime/Error.cpp index db1d8cc..ddd4bc4 100644 --- a/JavaScriptCore/runtime/Error.cpp +++ b/JavaScriptCore/runtime/Error.cpp @@ -97,6 +97,12 @@ JSObject* Error::create(ExecState* exec, ErrorType type, const char* message) return create(exec, type, message, -1, -1, NULL); } +JSObject* throwError(ExecState* exec, JSObject* error) +{ + exec->setException(error); + return error; +} + JSObject* throwError(ExecState* exec, ErrorType type) { JSObject* error = Error::create(exec, type, UString(), -1, -1, NULL); diff --git a/JavaScriptCore/runtime/Error.h b/JavaScriptCore/runtime/Error.h index adf7fdf..e959cff 100644 --- a/JavaScriptCore/runtime/Error.h +++ b/JavaScriptCore/runtime/Error.h @@ -59,6 +59,7 @@ namespace JSC { JSObject* throwError(ExecState*, ErrorType, const UString& message); JSObject* throwError(ExecState*, ErrorType, const char* message); JSObject* throwError(ExecState*, ErrorType); + JSObject* throwError(ExecState*, JSObject*); } // namespace JSC diff --git a/JavaScriptCore/runtime/ErrorConstructor.cpp b/JavaScriptCore/runtime/ErrorConstructor.cpp index 07b7e23..b9c3f58 100644 --- a/JavaScriptCore/runtime/ErrorConstructor.cpp +++ b/JavaScriptCore/runtime/ErrorConstructor.cpp @@ -29,7 +29,7 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ErrorConstructor); -ErrorConstructor::ErrorConstructor(ExecState* exec, PassRefPtr<Structure> structure, ErrorPrototype* errorPrototype) +ErrorConstructor::ErrorConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ErrorPrototype* errorPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, errorPrototype->classInfo()->className)) { // ECMA 15.11.3.1 Error.prototype diff --git a/JavaScriptCore/runtime/ErrorConstructor.h b/JavaScriptCore/runtime/ErrorConstructor.h index 2dd4124..e3d789b 100644 --- a/JavaScriptCore/runtime/ErrorConstructor.h +++ b/JavaScriptCore/runtime/ErrorConstructor.h @@ -30,7 +30,7 @@ namespace JSC { class ErrorConstructor : public InternalFunction { public: - ErrorConstructor(ExecState*, PassRefPtr<Structure>, ErrorPrototype*); + ErrorConstructor(ExecState*, NonNullPassRefPtr<Structure>, ErrorPrototype*); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/ErrorInstance.cpp b/JavaScriptCore/runtime/ErrorInstance.cpp index 2e2cdce..1cdb87a 100644 --- a/JavaScriptCore/runtime/ErrorInstance.cpp +++ b/JavaScriptCore/runtime/ErrorInstance.cpp @@ -25,7 +25,7 @@ namespace JSC { const ClassInfo ErrorInstance::info = { "Error", 0, 0, 0 }; -ErrorInstance::ErrorInstance(PassRefPtr<Structure> structure) +ErrorInstance::ErrorInstance(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { } diff --git a/JavaScriptCore/runtime/ErrorInstance.h b/JavaScriptCore/runtime/ErrorInstance.h index 6f9d262..9f53b51 100644 --- a/JavaScriptCore/runtime/ErrorInstance.h +++ b/JavaScriptCore/runtime/ErrorInstance.h @@ -27,7 +27,7 @@ namespace JSC { class ErrorInstance : public JSObject { public: - explicit ErrorInstance(PassRefPtr<Structure>); + explicit ErrorInstance(NonNullPassRefPtr<Structure>); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; diff --git a/JavaScriptCore/runtime/ErrorPrototype.cpp b/JavaScriptCore/runtime/ErrorPrototype.cpp index 599390e..a9a7a43 100644 --- a/JavaScriptCore/runtime/ErrorPrototype.cpp +++ b/JavaScriptCore/runtime/ErrorPrototype.cpp @@ -34,7 +34,7 @@ ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); static JSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); // ECMA 15.9.4 -ErrorPrototype::ErrorPrototype(ExecState* exec, PassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) +ErrorPrototype::ErrorPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) : ErrorInstance(structure) { // The constructor will be added later in ErrorConstructor's constructor diff --git a/JavaScriptCore/runtime/ErrorPrototype.h b/JavaScriptCore/runtime/ErrorPrototype.h index 53d12d9..a561590 100644 --- a/JavaScriptCore/runtime/ErrorPrototype.h +++ b/JavaScriptCore/runtime/ErrorPrototype.h @@ -29,7 +29,7 @@ namespace JSC { class ErrorPrototype : public ErrorInstance { public: - ErrorPrototype(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure); + ErrorPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/ExceptionHelpers.cpp b/JavaScriptCore/runtime/ExceptionHelpers.cpp index e63594c..5bead90 100644 --- a/JavaScriptCore/runtime/ExceptionHelpers.cpp +++ b/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -66,6 +66,11 @@ JSValue createStackOverflowError(ExecState* exec) return createError(exec, RangeError, "Maximum call stack size exceeded."); } +JSValue createTypeError(ExecState* exec, const char* message) +{ + return createError(exec, TypeError, message); +} + JSValue createUndefinedVariableError(ExecState* exec, const Identifier& ident, unsigned bytecodeOffset, CodeBlock* codeBlock) { int startOffset = 0; @@ -74,7 +79,7 @@ JSValue createUndefinedVariableError(ExecState* exec, const Identifier& ident, u int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); UString message = "Can't find variable: "; message.append(ident.ustring()); - JSObject* exception = Error::create(exec, ReferenceError, message, line, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL()); + JSObject* exception = Error::create(exec, ReferenceError, message, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); @@ -136,7 +141,7 @@ JSObject* createInvalidParamError(ExecState* exec, const char* op, JSValue value int divotPoint = 0; int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint, divotPoint + endOffset, value, message); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL()); + JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); @@ -157,7 +162,7 @@ JSObject* createNotAConstructorError(ExecState* exec, JSValue value, unsigned by startPoint++; UString errorMessage = createErrorMessage(exec, codeBlock, line, startPoint, divotPoint, value, "not a constructor"); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL()); + JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); @@ -171,7 +176,7 @@ JSValue createNotAFunctionError(ExecState* exec, JSValue value, unsigned bytecod int divotPoint = 0; int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint - startOffset, divotPoint, value, "not a function"); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL()); + JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); @@ -201,7 +206,7 @@ JSObject* createNotAnObjectError(ExecState* exec, JSNotAnObjectErrorStub* error, int divotPoint = 0; int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint - startOffset, divotPoint, error->isNull() ? jsNull() : jsUndefined(), "not an object"); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL()); + JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); diff --git a/JavaScriptCore/runtime/ExceptionHelpers.h b/JavaScriptCore/runtime/ExceptionHelpers.h index 4c5bec1..e739d09 100644 --- a/JavaScriptCore/runtime/ExceptionHelpers.h +++ b/JavaScriptCore/runtime/ExceptionHelpers.h @@ -44,6 +44,7 @@ namespace JSC { JSValue createInterruptedExecutionException(JSGlobalData*); JSValue createStackOverflowError(ExecState*); + JSValue createTypeError(ExecState*, const char* message); JSValue createUndefinedVariableError(ExecState*, const Identifier&, unsigned bytecodeOffset, CodeBlock*); JSNotAnObjectErrorStub* createNotAnObjectErrorStub(ExecState*, bool isNull); JSObject* createInvalidParamError(ExecState*, const char* op, JSValue, unsigned bytecodeOffset, CodeBlock*); diff --git a/JavaScriptCore/runtime/Executable.cpp b/JavaScriptCore/runtime/Executable.cpp new file mode 100644 index 0000000..7586746 --- /dev/null +++ b/JavaScriptCore/runtime/Executable.cpp @@ -0,0 +1,280 @@ +/* + * 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 "Executable.h" + +#include "BytecodeGenerator.h" +#include "CodeBlock.h" +#include "JIT.h" +#include "Parser.h" +#include "Vector.h" + +namespace JSC { + +#if ENABLE(JIT) +NativeExecutable::~NativeExecutable() +{ +} +#endif + +VPtrHackExecutable::~VPtrHackExecutable() +{ +} + +EvalExecutable::~EvalExecutable() +{ + delete m_evalCodeBlock; +} + +ProgramExecutable::~ProgramExecutable() +{ + delete m_programCodeBlock; +} + +FunctionExecutable::~FunctionExecutable() +{ + delete m_codeBlock; +} + +JSObject* EvalExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + int errLine; + UString errMsg; + RefPtr<EvalNode> evalNode = exec->globalData().parser->parse<EvalNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); + if (!evalNode) + return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); + recordParse(evalNode->features(), evalNode->lineNo(), evalNode->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + ASSERT(!m_evalCodeBlock); + m_evalCodeBlock = new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth()); + OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(evalNode.get(), globalObject->debugger(), scopeChain, m_evalCodeBlock->symbolTable(), m_evalCodeBlock)); + generator->generate(); + + evalNode->destroyData(); + return 0; +} + +JSObject* ProgramExecutable::checkSyntax(ExecState* exec) +{ + int errLine; + UString errMsg; + RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); + if (!programNode) + return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); + return 0; +} + +JSObject* ProgramExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + int errLine; + UString errMsg; + RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); + if (!programNode) + return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); + recordParse(programNode->features(), programNode->lineNo(), programNode->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + ASSERT(!m_programCodeBlock); + m_programCodeBlock = new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider()); + OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(programNode.get(), globalObject->debugger(), scopeChain, &globalObject->symbolTable(), m_programCodeBlock)); + generator->generate(); + + programNode->destroyData(); + return 0; +} + +void FunctionExecutable::compile(ExecState*, ScopeChainNode* scopeChainNode) +{ + JSGlobalData* globalData = scopeChainNode->globalData; + RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(globalData, 0, 0, m_source); + if (m_forceUsesArguments) + body->setUsesArguments(); + body->finishParsing(m_parameters, m_name); + recordParse(body->features(), body->lineNo(), body->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + ASSERT(!m_codeBlock); + m_codeBlock = new FunctionCodeBlock(this, FunctionCode, source().provider(), source().startOffset()); + OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(body.get(), globalObject->debugger(), scopeChain, m_codeBlock->symbolTable(), m_codeBlock)); + generator->generate(); + m_numParameters = m_codeBlock->m_numParameters; + ASSERT(m_numParameters); + m_numVariables = m_codeBlock->m_numVars; + + body->destroyData(); +} + +#if ENABLE(JIT) + +void EvalExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); + m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); + +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + codeBlock->discardBytecode(); +#endif +} + +void ProgramExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); + m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); + +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + codeBlock->discardBytecode(); +#endif +} + +void FunctionExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); + m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); + +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + codeBlock->discardBytecode(); +#endif +} + +#endif + +void FunctionExecutable::markAggregate(MarkStack& markStack) +{ + if (m_codeBlock) + m_codeBlock->markAggregate(markStack); +} + +ExceptionInfo* FunctionExecutable::reparseExceptionInfo(JSGlobalData* globalData, ScopeChainNode* scopeChainNode, CodeBlock* codeBlock) +{ + RefPtr<FunctionBodyNode> newFunctionBody = globalData->parser->parse<FunctionBodyNode>(globalData, 0, 0, m_source); + if (m_forceUsesArguments) + newFunctionBody->setUsesArguments(); + newFunctionBody->finishParsing(m_parameters, m_name); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + OwnPtr<CodeBlock> newCodeBlock(new FunctionCodeBlock(this, FunctionCode, source().provider(), source().startOffset())); + globalData->functionCodeBlockBeingReparsed = newCodeBlock.get(); + + OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(newFunctionBody.get(), globalObject->debugger(), scopeChain, newCodeBlock->symbolTable(), newCodeBlock.get())); + generator->setRegeneratingForExceptionInfo(static_cast<FunctionCodeBlock*>(codeBlock)); + generator->generate(); + + ASSERT(newCodeBlock->instructionCount() == codeBlock->instructionCount()); + +#if ENABLE(JIT) + JITCode newJITCode = JIT::compile(globalData, newCodeBlock.get()); + ASSERT(newJITCode.size() == generatedJITCode().size()); +#endif + + globalData->functionCodeBlockBeingReparsed = 0; + + return newCodeBlock->extractExceptionInfo(); +} + +ExceptionInfo* EvalExecutable::reparseExceptionInfo(JSGlobalData* globalData, ScopeChainNode* scopeChainNode, CodeBlock* codeBlock) +{ + RefPtr<EvalNode> newEvalBody = globalData->parser->parse<EvalNode>(globalData, 0, 0, m_source); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + OwnPtr<EvalCodeBlock> newCodeBlock(new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth())); + + OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(newEvalBody.get(), globalObject->debugger(), scopeChain, newCodeBlock->symbolTable(), newCodeBlock.get())); + generator->setRegeneratingForExceptionInfo(static_cast<EvalCodeBlock*>(codeBlock)); + generator->generate(); + + ASSERT(newCodeBlock->instructionCount() == codeBlock->instructionCount()); + +#if ENABLE(JIT) + JITCode newJITCode = JIT::compile(globalData, newCodeBlock.get()); + ASSERT(newJITCode.size() == generatedJITCode().size()); +#endif + + return newCodeBlock->extractExceptionInfo(); +} + +void FunctionExecutable::recompile(ExecState*) +{ + delete m_codeBlock; + m_codeBlock = 0; + m_numParameters = NUM_PARAMETERS_NOT_COMPILED; +#if ENABLE(JIT) + m_jitCode = JITCode(); +#endif +} + +PassRefPtr<FunctionExecutable> FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, int* errLine, UString* errMsg) +{ + RefPtr<ProgramNode> program = exec->globalData().parser->parse<ProgramNode>(&exec->globalData(), debugger, exec, source, errLine, errMsg); + if (!program) + return 0; + + StatementNode* exprStatement = program->singleStatement(); + ASSERT(exprStatement); + ASSERT(exprStatement->isExprStatement()); + if (!exprStatement || !exprStatement->isExprStatement()) + return 0; + + ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr(); + ASSERT(funcExpr); + ASSERT(funcExpr->isFuncExprNode()); + if (!funcExpr || !funcExpr->isFuncExprNode()) + return 0; + + FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body(); + ASSERT(body); + return FunctionExecutable::create(&exec->globalData(), functionName, body->source(), body->usesArguments(), body->parameters(), body->lineNo(), body->lastLine()); +} + +UString FunctionExecutable::paramString() const +{ + FunctionParameters& parameters = *m_parameters; + UString s(""); + for (size_t pos = 0; pos < parameters.size(); ++pos) { + if (!s.isEmpty()) + s += ", "; + s += parameters[pos].ustring(); + } + + return s; +} + +}; + + diff --git a/JavaScriptCore/runtime/Executable.h b/JavaScriptCore/runtime/Executable.h new file mode 100644 index 0000000..f74abe9 --- /dev/null +++ b/JavaScriptCore/runtime/Executable.h @@ -0,0 +1,356 @@ +/* + * 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 Executable_h +#define Executable_h + +#include "JSFunction.h" +#include "Interpreter.h" +#include "Nodes.h" +#include "SamplingTool.h" + +namespace JSC { + + class CodeBlock; + class Debugger; + class EvalCodeBlock; + class ProgramCodeBlock; + class ScopeChainNode; + + struct ExceptionInfo; + + class ExecutableBase : public RefCounted<ExecutableBase> { + friend class JIT; + + protected: + static const int NUM_PARAMETERS_IS_HOST = 0; + static const int NUM_PARAMETERS_NOT_COMPILED = -1; + + public: + ExecutableBase(int numParameters) + : m_numParameters(numParameters) + { + } + + virtual ~ExecutableBase() {} + + bool isHostFunction() const { return m_numParameters == NUM_PARAMETERS_IS_HOST; } + + protected: + int m_numParameters; + +#if ENABLE(JIT) + public: + JITCode& generatedJITCode() + { + ASSERT(m_jitCode); + return m_jitCode; + } + + ExecutablePool* getExecutablePool() + { + return m_jitCode.getExecutablePool(); + } + + protected: + JITCode m_jitCode; +#endif + }; + +#if ENABLE(JIT) + class NativeExecutable : public ExecutableBase { + public: + NativeExecutable(ExecState* exec) + : ExecutableBase(NUM_PARAMETERS_IS_HOST) + { + m_jitCode = JITCode(JITCode::HostFunction(exec->globalData().jitStubs.ctiNativeCallThunk())); + } + + ~NativeExecutable(); + }; +#endif + + class VPtrHackExecutable : public ExecutableBase { + public: + VPtrHackExecutable() + : ExecutableBase(NUM_PARAMETERS_IS_HOST) + { + } + + ~VPtrHackExecutable(); + }; + + class ScriptExecutable : public ExecutableBase { + public: + ScriptExecutable(JSGlobalData* globalData, const SourceCode& source) + : ExecutableBase(NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(0) + { +#if ENABLE(CODEBLOCK_SAMPLING) + if (SamplingTool* sampler = globalData->interpreter->sampler()) + sampler->notifyOfScope(this); +#else + UNUSED_PARAM(globalData); +#endif + } + + ScriptExecutable(ExecState* exec, const SourceCode& source) + : ExecutableBase(NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(0) + { +#if ENABLE(CODEBLOCK_SAMPLING) + if (SamplingTool* sampler = exec->globalData().interpreter->sampler()) + sampler->notifyOfScope(this); +#else + UNUSED_PARAM(exec); +#endif + } + + const SourceCode& source() { return m_source; } + intptr_t sourceID() const { return m_source.provider()->asID(); } + const UString& sourceURL() const { return m_source.provider()->url(); } + int lineNo() const { return m_firstLine; } + int lastLine() const { return m_lastLine; } + + bool usesEval() const { return m_features & EvalFeature; } + bool usesArguments() const { return m_features & ArgumentsFeature; } + bool needsActivation() const { return m_features & (EvalFeature | ClosureFeature | WithFeature | CatchFeature); } + + virtual ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*) = 0; + + protected: + void recordParse(CodeFeatures features, int firstLine, int lastLine) + { + m_features = features; + m_firstLine = firstLine; + m_lastLine = lastLine; + } + + SourceCode m_source; + CodeFeatures m_features; + int m_firstLine; + int m_lastLine; + }; + + class EvalExecutable : public ScriptExecutable { + public: + + ~EvalExecutable(); + + EvalCodeBlock& bytecode(ExecState* exec, ScopeChainNode* scopeChainNode) + { + if (!m_evalCodeBlock) { + JSObject* error = compile(exec, scopeChainNode); + ASSERT_UNUSED(!error, error); + } + return *m_evalCodeBlock; + } + + JSObject* compile(ExecState*, ScopeChainNode*); + + ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*); + static PassRefPtr<EvalExecutable> create(ExecState* exec, const SourceCode& source) { return adoptRef(new EvalExecutable(exec, source)); } + + private: + EvalExecutable(ExecState* exec, const SourceCode& source) + : ScriptExecutable(exec, source) + , m_evalCodeBlock(0) + { + } + EvalCodeBlock* m_evalCodeBlock; + +#if ENABLE(JIT) + public: + JITCode& jitCode(ExecState* exec, ScopeChainNode* scopeChainNode) + { + if (!m_jitCode) + generateJITCode(exec, scopeChainNode); + return m_jitCode; + } + + private: + void generateJITCode(ExecState*, ScopeChainNode*); +#endif + }; + + class ProgramExecutable : public ScriptExecutable { + public: + static PassRefPtr<ProgramExecutable> create(ExecState* exec, const SourceCode& source) + { + return adoptRef(new ProgramExecutable(exec, source)); + } + + ~ProgramExecutable(); + + ProgramCodeBlock& bytecode(ExecState* exec, ScopeChainNode* scopeChainNode) + { + if (!m_programCodeBlock) { + JSObject* error = compile(exec, scopeChainNode); + ASSERT_UNUSED(!error, error); + } + return *m_programCodeBlock; + } + + JSObject* checkSyntax(ExecState*); + JSObject* compile(ExecState*, ScopeChainNode*); + + // CodeBlocks for program code are transient and therefore do not gain from from throwing out there exception information. + ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*) { ASSERT_NOT_REACHED(); return 0; } + + private: + ProgramExecutable(ExecState* exec, const SourceCode& source) + : ScriptExecutable(exec, source) + , m_programCodeBlock(0) + { + } + ProgramCodeBlock* m_programCodeBlock; + +#if ENABLE(JIT) + public: + JITCode& jitCode(ExecState* exec, ScopeChainNode* scopeChainNode) + { + if (!m_jitCode) + generateJITCode(exec, scopeChainNode); + return m_jitCode; + } + + private: + void generateJITCode(ExecState*, ScopeChainNode*); +#endif + }; + + class FunctionExecutable : public ScriptExecutable { + friend class JIT; + public: + static PassRefPtr<FunctionExecutable> create(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) + { + return adoptRef(new FunctionExecutable(exec, name, source, forceUsesArguments, parameters, firstLine, lastLine)); + } + + static PassRefPtr<FunctionExecutable> create(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) + { + return adoptRef(new FunctionExecutable(globalData, name, source, forceUsesArguments, parameters, firstLine, lastLine)); + } + + ~FunctionExecutable(); + + JSFunction* make(ExecState* exec, ScopeChainNode* scopeChain) + { + return new (exec) JSFunction(exec, this, scopeChain); + } + + CodeBlock& bytecode(ExecState* exec, ScopeChainNode* scopeChainNode) + { + ASSERT(scopeChainNode); + if (!m_codeBlock) + compile(exec, scopeChainNode); + return *m_codeBlock; + } + + bool isGenerated() const + { + return m_codeBlock; + } + + CodeBlock& generatedBytecode() + { + ASSERT(m_codeBlock); + return *m_codeBlock; + } + + const Identifier& name() { return m_name; } + size_t parameterCount() const { return m_parameters->size(); } + size_t variableCount() const { return m_numVariables; } + UString paramString() const; + + void recompile(ExecState*); + ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*); + void markAggregate(MarkStack& markStack); + static PassRefPtr<FunctionExecutable> fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, int* errLine = 0, UString* errMsg = 0); + + private: + FunctionExecutable(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) + : ScriptExecutable(globalData, source) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_codeBlock(0) + , m_name(name) + , m_numVariables(0) + { + m_firstLine = firstLine; + m_lastLine = lastLine; + } + + FunctionExecutable(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) + : ScriptExecutable(exec, source) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_codeBlock(0) + , m_name(name) + , m_numVariables(0) + { + m_firstLine = firstLine; + m_lastLine = lastLine; + } + + void compile(ExecState*, ScopeChainNode*); + + bool m_forceUsesArguments; + RefPtr<FunctionParameters> m_parameters; + CodeBlock* m_codeBlock; + Identifier m_name; + size_t m_numVariables; + +#if ENABLE(JIT) + public: + JITCode& jitCode(ExecState* exec, ScopeChainNode* scopeChainNode) + { + if (!m_jitCode) + generateJITCode(exec, scopeChainNode); + return m_jitCode; + } + + private: + void generateJITCode(ExecState*, ScopeChainNode*); +#endif + }; + + inline FunctionExecutable* JSFunction::jsExecutable() const + { + ASSERT(!isHostFunctionNonInline()); + return static_cast<FunctionExecutable*>(m_executable.get()); + } + + inline bool JSFunction::isHostFunction() const + { + ASSERT(m_executable); + return m_executable->isHostFunction(); + } + +} + +#endif diff --git a/JavaScriptCore/runtime/FunctionConstructor.cpp b/JavaScriptCore/runtime/FunctionConstructor.cpp index f4f5cc8..9d88400 100644 --- a/JavaScriptCore/runtime/FunctionConstructor.cpp +++ b/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -34,7 +34,7 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(FunctionConstructor); -FunctionConstructor::FunctionConstructor(ExecState* exec, PassRefPtr<Structure> structure, FunctionPrototype* functionPrototype) +FunctionConstructor::FunctionConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, FunctionPrototype* functionPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, functionPrototype->classInfo()->className)) { putDirectWithoutTransition(exec->propertyNames().prototype, functionPrototype, DontEnum | DontDelete | ReadOnly); @@ -66,32 +66,6 @@ CallType FunctionConstructor::getCallData(CallData& callData) return CallTypeHost; } -FunctionBodyNode* extractFunctionBody(ProgramNode* program) -{ - if (!program) - return 0; - - StatementVector& children = program->children(); - if (children.size() != 1) - return 0; - - StatementNode* exprStatement = children[0]; - ASSERT(exprStatement); - ASSERT(exprStatement->isExprStatement()); - if (!exprStatement || !exprStatement->isExprStatement()) - return 0; - - ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr(); - ASSERT(funcExpr); - ASSERT(funcExpr->isFuncExprNode()); - if (!funcExpr || !funcExpr->isFuncExprNode()) - return 0; - - FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body(); - ASSERT(body); - return body; -} - // ECMA 15.3.2 The Function Constructor JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifier& functionName, const UString& sourceURL, int lineNumber) { @@ -113,15 +87,13 @@ JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifi int errLine; UString errMsg; SourceCode source = makeSource(program, sourceURL, lineNumber); - RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); - - FunctionBodyNode* body = extractFunctionBody(programNode.get()); - if (!body) + RefPtr<FunctionExecutable> function = FunctionExecutable::fromGlobalCode(functionName, exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); + if (!function) return throwError(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url()); JSGlobalObject* globalObject = exec->lexicalGlobalObject(); - ScopeChain scopeChain(globalObject, globalObject->globalData(), exec->globalThisValue()); - return new (exec) JSFunction(exec, functionName, body, scopeChain.node()); + ScopeChain scopeChain(globalObject, globalObject->globalData(), globalObject, exec->globalThisValue()); + return new (exec) JSFunction(exec, function, scopeChain.node()); } // ECMA 15.3.2 The Function Constructor diff --git a/JavaScriptCore/runtime/FunctionConstructor.h b/JavaScriptCore/runtime/FunctionConstructor.h index 124b354..197f320 100644 --- a/JavaScriptCore/runtime/FunctionConstructor.h +++ b/JavaScriptCore/runtime/FunctionConstructor.h @@ -26,12 +26,10 @@ namespace JSC { class FunctionPrototype; - class ProgramNode; - class FunctionBodyNode; class FunctionConstructor : public InternalFunction { public: - FunctionConstructor(ExecState*, PassRefPtr<Structure>, FunctionPrototype*); + FunctionConstructor(ExecState*, NonNullPassRefPtr<Structure>, FunctionPrototype*); private: virtual ConstructType getConstructData(ConstructData&); @@ -41,8 +39,6 @@ namespace JSC { JSObject* constructFunction(ExecState*, const ArgList&, const Identifier& functionName, const UString& sourceURL, int lineNumber); JSObject* constructFunction(ExecState*, const ArgList&); - FunctionBodyNode* extractFunctionBody(ProgramNode*); - } // namespace JSC #endif // FunctionConstructor_h diff --git a/JavaScriptCore/runtime/FunctionPrototype.cpp b/JavaScriptCore/runtime/FunctionPrototype.cpp index 9ba2144..45f17b1 100644 --- a/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,7 +37,7 @@ static JSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*, JSObject*, JS static JSValue JSC_HOST_CALL functionProtoFuncApply(ExecState*, JSObject*, JSValue, const ArgList&); static JSValue JSC_HOST_CALL functionProtoFuncCall(ExecState*, JSObject*, JSValue, const ArgList&); -FunctionPrototype::FunctionPrototype(ExecState* exec, PassRefPtr<Structure> structure) +FunctionPrototype::FunctionPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure) : InternalFunction(&exec->globalData(), structure, exec->propertyNames().nullIdentifier) { putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum); @@ -84,16 +84,17 @@ static inline void insertSemicolonIfNeeded(UString& functionBody) JSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (thisValue.isObject(&JSFunction::info)) { + if (thisValue.inherits(&JSFunction::info)) { JSFunction* function = asFunction(thisValue); if (!function->isHostFunction()) { - UString functionBody = function->body()->toSourceString(); - insertSemicolonIfNeeded(functionBody); - return jsString(exec, "function " + function->name(&exec->globalData()) + "(" + function->body()->paramString() + ") " + functionBody); + FunctionExecutable* executable = function->jsExecutable(); + UString sourceString = executable->source().toString(); + insertSemicolonIfNeeded(sourceString); + return jsString(exec, "function " + function->name(&exec->globalData()) + "(" + executable->paramString() + ") " + sourceString); } } - if (thisValue.isObject(&InternalFunction::info)) { + if (thisValue.inherits(&InternalFunction::info)) { InternalFunction* function = asInternalFunction(thisValue); return jsString(exec, "function " + function->name(&exec->globalData()) + "() {\n [native code]\n}"); } diff --git a/JavaScriptCore/runtime/FunctionPrototype.h b/JavaScriptCore/runtime/FunctionPrototype.h index 607ddab..0e38549 100644 --- a/JavaScriptCore/runtime/FunctionPrototype.h +++ b/JavaScriptCore/runtime/FunctionPrototype.h @@ -29,12 +29,12 @@ namespace JSC { class FunctionPrototype : public InternalFunction { public: - FunctionPrototype(ExecState*, PassRefPtr<Structure>); + FunctionPrototype(ExecState*, NonNullPassRefPtr<Structure>); void addFunctionProperties(ExecState*, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction); static PassRefPtr<Structure> createStructure(JSValue proto) { - return Structure::create(proto, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot)); + return Structure::create(proto, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark | HasDefaultGetPropertyNames)); } private: diff --git a/JavaScriptCore/runtime/GetterSetter.cpp b/JavaScriptCore/runtime/GetterSetter.cpp index cc85354..7e54053 100644 --- a/JavaScriptCore/runtime/GetterSetter.cpp +++ b/JavaScriptCore/runtime/GetterSetter.cpp @@ -32,50 +32,12 @@ void GetterSetter::markChildren(MarkStack& markStack) { JSCell::markChildren(markStack); - if (m_getter && !m_getter->marked()) + if (m_getter) markStack.append(m_getter); - if (m_setter && !m_setter->marked()) + if (m_setter) markStack.append(m_setter); } -JSValue GetterSetter::toPrimitive(ExecState*, PreferredPrimitiveType) const -{ - ASSERT_NOT_REACHED(); - return jsNull(); -} - -bool GetterSetter::getPrimitiveNumber(ExecState*, double& number, JSValue& value) -{ - ASSERT_NOT_REACHED(); - number = 0; - value = JSValue(); - return true; -} - -bool GetterSetter::toBoolean(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return false; -} - -double GetterSetter::toNumber(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return 0.0; -} - -UString GetterSetter::toString(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return UString::null(); -} - -JSObject* GetterSetter::toObject(ExecState* exec) const -{ - ASSERT_NOT_REACHED(); - return jsNull().toObject(exec); -} - bool GetterSetter::isGetterSetter() const { return true; diff --git a/JavaScriptCore/runtime/GetterSetter.h b/JavaScriptCore/runtime/GetterSetter.h index b7a8794..73dd854 100644 --- a/JavaScriptCore/runtime/GetterSetter.h +++ b/JavaScriptCore/runtime/GetterSetter.h @@ -55,13 +55,6 @@ namespace JSC { private: virtual bool isGetterSetter() const; - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; - virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); - virtual bool toBoolean(ExecState*) const; - virtual double toNumber(ExecState*) const; - virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; - JSObject* m_getter; JSObject* m_setter; }; diff --git a/JavaScriptCore/runtime/GlobalEvalFunction.cpp b/JavaScriptCore/runtime/GlobalEvalFunction.cpp index 3074f95..c26002b 100644 --- a/JavaScriptCore/runtime/GlobalEvalFunction.cpp +++ b/JavaScriptCore/runtime/GlobalEvalFunction.cpp @@ -32,7 +32,7 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(GlobalEvalFunction); -GlobalEvalFunction::GlobalEvalFunction(ExecState* exec, PassRefPtr<Structure> structure, int len, const Identifier& name, NativeFunction function, JSGlobalObject* cachedGlobalObject) +GlobalEvalFunction::GlobalEvalFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, int len, const Identifier& name, NativeFunction function, JSGlobalObject* cachedGlobalObject) : PrototypeFunction(exec, structure, len, name, function) , m_cachedGlobalObject(cachedGlobalObject) { diff --git a/JavaScriptCore/runtime/GlobalEvalFunction.h b/JavaScriptCore/runtime/GlobalEvalFunction.h index cdba4a0..b62ad3e 100644 --- a/JavaScriptCore/runtime/GlobalEvalFunction.h +++ b/JavaScriptCore/runtime/GlobalEvalFunction.h @@ -32,9 +32,14 @@ namespace JSC { class GlobalEvalFunction : public PrototypeFunction { public: - GlobalEvalFunction(ExecState*, PassRefPtr<Structure>, int len, const Identifier&, NativeFunction, JSGlobalObject* expectedThisObject); + GlobalEvalFunction(ExecState*, NonNullPassRefPtr<Structure>, int len, const Identifier&, NativeFunction, JSGlobalObject* expectedThisObject); JSGlobalObject* cachedGlobalObject() const { return m_cachedGlobalObject; } + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot)); + } + private: virtual void markChildren(MarkStack&); diff --git a/JavaScriptCore/runtime/Identifier.h b/JavaScriptCore/runtime/Identifier.h index 631cf42..2249179 100644 --- a/JavaScriptCore/runtime/Identifier.h +++ b/JavaScriptCore/runtime/Identifier.h @@ -54,6 +54,8 @@ namespace JSC { const char* ascii() const { return _ustring.ascii(); } static Identifier from(ExecState* exec, unsigned y) { return Identifier(exec, UString::from(y)); } + static Identifier from(ExecState* exec, int y) { return Identifier(exec, UString::from(y)); } + static Identifier from(ExecState* exec, double y) { return Identifier(exec, UString::from(y)); } bool isNull() const { return _ustring.isNull(); } bool isEmpty() const { return _ustring.isEmpty(); } diff --git a/JavaScriptCore/runtime/InternalFunction.cpp b/JavaScriptCore/runtime/InternalFunction.cpp index b5c9571..2ba2984 100644 --- a/JavaScriptCore/runtime/InternalFunction.cpp +++ b/JavaScriptCore/runtime/InternalFunction.cpp @@ -37,7 +37,7 @@ const ClassInfo* InternalFunction::classInfo() const return &info; } -InternalFunction::InternalFunction(JSGlobalData* globalData, PassRefPtr<Structure> structure, const Identifier& name) +InternalFunction::InternalFunction(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure, const Identifier& name) : JSObject(structure) { putDirect(globalData->propertyNames->name, jsString(globalData, name.ustring()), DontDelete | ReadOnly | DontEnum); diff --git a/JavaScriptCore/runtime/InternalFunction.h b/JavaScriptCore/runtime/InternalFunction.h index 310644c..fdd5cc1 100644 --- a/JavaScriptCore/runtime/InternalFunction.h +++ b/JavaScriptCore/runtime/InternalFunction.h @@ -42,12 +42,12 @@ namespace JSC { static PassRefPtr<Structure> createStructure(JSValue proto) { - return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot)); + return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot | HasDefaultMark)); } protected: - InternalFunction(PassRefPtr<Structure> structure) : JSObject(structure) { } - InternalFunction(JSGlobalData*, PassRefPtr<Structure>, const Identifier&); + InternalFunction(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { } + InternalFunction(JSGlobalData*, NonNullPassRefPtr<Structure>, const Identifier&); private: virtual CallType getCallData(CallData&) = 0; diff --git a/JavaScriptCore/runtime/JSAPIValueWrapper.cpp b/JavaScriptCore/runtime/JSAPIValueWrapper.cpp index 475fad5..e83724a 100644 --- a/JavaScriptCore/runtime/JSAPIValueWrapper.cpp +++ b/JavaScriptCore/runtime/JSAPIValueWrapper.cpp @@ -28,40 +28,4 @@ namespace JSC { -JSValue JSAPIValueWrapper::toPrimitive(ExecState*, PreferredPrimitiveType) const -{ - ASSERT_NOT_REACHED(); - return JSValue(); -} - -bool JSAPIValueWrapper::getPrimitiveNumber(ExecState*, double&, JSValue&) -{ - ASSERT_NOT_REACHED(); - return false; -} - -bool JSAPIValueWrapper::toBoolean(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return false; -} - -double JSAPIValueWrapper::toNumber(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return 0; -} - -UString JSAPIValueWrapper::toString(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return UString(); -} - -JSObject* JSAPIValueWrapper::toObject(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return 0; -} - } // namespace JSC diff --git a/JavaScriptCore/runtime/JSAPIValueWrapper.h b/JavaScriptCore/runtime/JSAPIValueWrapper.h index 21a9710..88a8493 100644 --- a/JavaScriptCore/runtime/JSAPIValueWrapper.h +++ b/JavaScriptCore/runtime/JSAPIValueWrapper.h @@ -37,12 +37,6 @@ namespace JSC { virtual bool isAPIValueWrapper() const { return true; } - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; - virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue&); - virtual bool toBoolean(ExecState*) const; - virtual double toNumber(ExecState*) const; - virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; static PassRefPtr<Structure> createStructure(JSValue prototype) { return Structure::create(prototype, TypeInfo(CompoundType)); @@ -54,6 +48,7 @@ namespace JSC { : JSCell(exec->globalData().apiWrapperStructure.get()) , m_value(value) { + ASSERT(!value.isCell()); } JSValue m_value; diff --git a/JavaScriptCore/runtime/JSActivation.cpp b/JavaScriptCore/runtime/JSActivation.cpp index 87adbcd..22fdaaf 100644 --- a/JavaScriptCore/runtime/JSActivation.cpp +++ b/JavaScriptCore/runtime/JSActivation.cpp @@ -39,8 +39,8 @@ ASSERT_CLASS_FITS_IN_CELL(JSActivation); const ClassInfo JSActivation::info = { "JSActivation", 0, 0, 0 }; -JSActivation::JSActivation(CallFrame* callFrame, PassRefPtr<FunctionBodyNode> functionBody) - : Base(callFrame->globalData().activationStructure, new JSActivationData(functionBody, callFrame->registers())) +JSActivation::JSActivation(CallFrame* callFrame, NonNullPassRefPtr<FunctionExecutable> functionExecutable) + : Base(callFrame->globalData().activationStructure, new JSActivationData(functionExecutable, callFrame->registers())) { } @@ -57,12 +57,12 @@ void JSActivation::markChildren(MarkStack& markStack) if (!registerArray) return; - size_t numParametersMinusThis = d()->functionBody->generatedBytecode().m_numParameters - 1; + size_t numParametersMinusThis = d()->functionExecutable->parameterCount(); size_t count = numParametersMinusThis; markStack.appendValues(registerArray, count); - size_t numVars = d()->functionBody->generatedBytecode().m_numVars; + size_t numVars = d()->functionExecutable->variableCount(); // Skip the call frame, which sits between the parameters and vars. markStack.appendValues(registerArray + count + RegisterFile::CallFrameHeaderSize, numVars, MayContainNullValues); @@ -136,14 +136,14 @@ JSObject* JSActivation::toThisObject(ExecState* exec) const bool JSActivation::isDynamicScope() const { - return d()->functionBody->usesEval(); + return d()->functionExecutable->usesEval(); } JSValue JSActivation::argumentsGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) { JSActivation* activation = asActivation(slot.slotBase()); - if (activation->d()->functionBody->usesArguments()) { + if (activation->d()->functionExecutable->usesArguments()) { PropertySlot slot; activation->symbolTableGet(exec->propertyNames().arguments, slot); return slot.getValue(exec, exec->propertyNames().arguments); @@ -156,7 +156,7 @@ JSValue JSActivation::argumentsGetter(ExecState* exec, const Identifier&, const arguments->copyRegisters(); callFrame->setCalleeArguments(arguments); } - ASSERT(arguments->isObject(&Arguments::info)); + ASSERT(arguments->inherits(&Arguments::info)); return arguments; } diff --git a/JavaScriptCore/runtime/JSActivation.h b/JavaScriptCore/runtime/JSActivation.h index 6a08439..583b988 100644 --- a/JavaScriptCore/runtime/JSActivation.h +++ b/JavaScriptCore/runtime/JSActivation.h @@ -43,7 +43,7 @@ namespace JSC { class JSActivation : public JSVariableObject { typedef JSVariableObject Base; public: - JSActivation(CallFrame*, PassRefPtr<FunctionBodyNode>); + JSActivation(CallFrame*, NonNullPassRefPtr<FunctionExecutable>); virtual ~JSActivation(); virtual void markChildren(MarkStack&); @@ -70,13 +70,20 @@ namespace JSC { private: struct JSActivationData : public JSVariableObjectData { - JSActivationData(PassRefPtr<FunctionBodyNode> functionBody, Register* registers) - : JSVariableObjectData(&functionBody->generatedBytecode().symbolTable(), registers) - , functionBody(functionBody) + JSActivationData(NonNullPassRefPtr<FunctionExecutable> _functionExecutable, Register* registers) + : JSVariableObjectData(_functionExecutable->generatedBytecode().symbolTable(), registers) + , functionExecutable(_functionExecutable) { + // We have to manually ref and deref the symbol table as JSVariableObjectData + // doesn't know about SharedSymbolTable + functionExecutable->generatedBytecode().sharedSymbolTable()->ref(); + } + ~JSActivationData() + { + static_cast<SharedSymbolTable*>(symbolTable)->deref(); } - RefPtr<FunctionBodyNode> functionBody; + RefPtr<FunctionExecutable> functionExecutable; }; static JSValue argumentsGetter(ExecState*, const Identifier&, const PropertySlot&); diff --git a/JavaScriptCore/runtime/JSArray.cpp b/JavaScriptCore/runtime/JSArray.cpp index 7d7d4c4..fd9e7b2 100644 --- a/JavaScriptCore/runtime/JSArray.cpp +++ b/JavaScriptCore/runtime/JSArray.cpp @@ -25,6 +25,8 @@ #include "ArrayPrototype.h" #include "CachedCall.h" +#include "Error.h" +#include "Executable.h" #include "PropertyNameArray.h" #include <wtf/AVLTree.h> #include <wtf/Assertions.h> @@ -128,27 +130,25 @@ inline void JSArray::checkConsistency(ConsistencyCheckType) #endif -JSArray::JSArray(PassRefPtr<Structure> structure) +JSArray::JSArray(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { unsigned initialCapacity = 0; m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); - m_storage->m_vectorLength = initialCapacity; - - m_fastAccessCutoff = 0; + m_vectorLength = initialCapacity; checkConsistency(); } -JSArray::JSArray(PassRefPtr<Structure> structure, unsigned initialLength) +JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength) : JSObject(structure) { unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX); m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); m_storage->m_length = initialLength; - m_storage->m_vectorLength = initialCapacity; + m_vectorLength = initialCapacity; m_storage->m_numValuesInVector = 0; m_storage->m_sparseValueMap = 0; m_storage->lazyCreationData = 0; @@ -157,21 +157,19 @@ JSArray::JSArray(PassRefPtr<Structure> structure, unsigned initialLength) for (size_t i = 0; i < initialCapacity; ++i) vector[i] = JSValue(); - m_fastAccessCutoff = 0; - checkConsistency(); Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue)); } -JSArray::JSArray(PassRefPtr<Structure> structure, const ArgList& list) +JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list) : JSObject(structure) { unsigned initialCapacity = list.size(); m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); m_storage->m_length = initialCapacity; - m_storage->m_vectorLength = initialCapacity; + m_vectorLength = initialCapacity; m_storage->m_numValuesInVector = initialCapacity; m_storage->m_sparseValueMap = 0; @@ -180,8 +178,6 @@ JSArray::JSArray(PassRefPtr<Structure> structure, const ArgList& list) for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i) m_storage->m_vector[i] = *it; - m_fastAccessCutoff = initialCapacity; - checkConsistency(); Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity)); @@ -205,7 +201,7 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot return false; } - if (i < storage->m_vectorLength) { + if (i < m_vectorLength) { JSValue& valueSlot = storage->m_vector[i]; if (valueSlot) { slot.setValueSlot(&valueSlot); @@ -221,7 +217,7 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot } } - return false; + return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot); } bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -239,6 +235,37 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName return JSObject::getOwnPropertySlot(exec, propertyName, slot); } +bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(exec, length()), DontDelete | DontEnum); + return true; + } + + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(&isArrayIndex); + if (isArrayIndex) { + if (i >= m_storage->m_length) + return false; + if (i < m_vectorLength) { + JSValue& value = m_storage->m_vector[i]; + if (value) { + descriptor.setDescriptor(value, 0); + return true; + } + } else if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { + if (i >= MIN_SPARSE_ARRAY_INDEX) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->end()) { + descriptor.setDescriptor(it->second, 0); + return true; + } + } + } + } + return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + // ECMA 15.4.5.1 void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { @@ -272,7 +299,7 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue value) m_storage->m_length = length; } - if (i < m_storage->m_vectorLength) { + if (i < m_vectorLength) { JSValue& valueSlot = m_storage->m_vector[i]; if (valueSlot) { valueSlot = value; @@ -280,8 +307,7 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue value) return; } valueSlot = value; - if (++m_storage->m_numValuesInVector == m_storage->m_length) - m_fastAccessCutoff = m_storage->m_length; + ++m_storage->m_numValuesInVector; checkConsistency(); return; } @@ -319,8 +345,7 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu if (increaseVectorLength(i + 1)) { storage = m_storage; storage->m_vector[i] = value; - if (++storage->m_numValuesInVector == storage->m_length) - m_fastAccessCutoff = storage->m_length; + ++storage->m_numValuesInVector; checkConsistency(); } else throwOutOfMemoryError(exec); @@ -330,7 +355,7 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu // Decide how many values it would be best to move from the map. unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; unsigned newVectorLength = increasedVectorLength(i + 1); - for (unsigned j = max(storage->m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) + for (unsigned j = max(m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) newNumValuesInVector += map->contains(j); if (i >= MIN_SPARSE_ARRAY_INDEX) newNumValuesInVector -= map->contains(i); @@ -348,13 +373,12 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu } } - storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength))); - if (!storage) { + if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) { throwOutOfMemoryError(exec); return; } - unsigned vectorLength = storage->m_vectorLength; + unsigned vectorLength = m_vectorLength; Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); @@ -372,7 +396,7 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu storage->m_vector[i] = value; - storage->m_vectorLength = newVectorLength; + m_vectorLength = newVectorLength; storage->m_numValuesInVector = newNumValuesInVector; m_storage = storage; @@ -399,7 +423,7 @@ bool JSArray::deleteProperty(ExecState* exec, unsigned i) ArrayStorage* storage = m_storage; - if (i < storage->m_vectorLength) { + if (i < m_vectorLength) { JSValue& valueSlot = storage->m_vector[i]; if (!valueSlot) { checkConsistency(); @@ -407,8 +431,6 @@ bool JSArray::deleteProperty(ExecState* exec, unsigned i) } valueSlot = JSValue(); --storage->m_numValuesInVector; - if (m_fastAccessCutoff > i) - m_fastAccessCutoff = i; checkConsistency(); return true; } @@ -432,7 +454,7 @@ bool JSArray::deleteProperty(ExecState* exec, unsigned i) return false; } -void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) { // FIXME: Filling PropertyNameArray with an identifier for every integer // is incredibly inefficient for large arrays. We need a different approach, @@ -440,7 +462,7 @@ void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames ArrayStorage* storage = m_storage; - unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength); + unsigned usedVectorLength = min(storage->m_length, m_vectorLength); for (unsigned i = 0; i < usedVectorLength; ++i) { if (storage->m_vector[i]) propertyNames.add(Identifier::from(exec, i)); @@ -452,7 +474,7 @@ void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames propertyNames.add(Identifier::from(exec, it->first)); } - JSObject::getPropertyNames(exec, propertyNames); + JSObject::getOwnPropertyNames(exec, propertyNames); } bool JSArray::increaseVectorLength(unsigned newLength) @@ -462,17 +484,16 @@ bool JSArray::increaseVectorLength(unsigned newLength) ArrayStorage* storage = m_storage; - unsigned vectorLength = storage->m_vectorLength; + unsigned vectorLength = m_vectorLength; ASSERT(newLength > vectorLength); ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX); unsigned newVectorLength = increasedVectorLength(newLength); - storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength))); - if (!storage) + if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) return false; Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); - storage->m_vectorLength = newVectorLength; + m_vectorLength = newVectorLength; for (unsigned i = vectorLength; i < newVectorLength; ++i) storage->m_vector[i] = JSValue(); @@ -490,10 +511,7 @@ void JSArray::setLength(unsigned newLength) unsigned length = m_storage->m_length; if (newLength < length) { - if (m_fastAccessCutoff > newLength) - m_fastAccessCutoff = newLength; - - unsigned usedVectorLength = min(length, storage->m_vectorLength); + unsigned usedVectorLength = min(length, m_vectorLength); for (unsigned i = newLength; i < usedVectorLength; ++i) { JSValue& valueSlot = storage->m_vector[i]; bool hadValue = valueSlot; @@ -532,20 +550,13 @@ JSValue JSArray::pop() JSValue result; - if (m_fastAccessCutoff > length) { - JSValue& valueSlot = m_storage->m_vector[length]; - result = valueSlot; - ASSERT(result); - valueSlot = JSValue(); - --m_storage->m_numValuesInVector; - m_fastAccessCutoff = length; - } else if (length < m_storage->m_vectorLength) { + if (length < m_vectorLength) { JSValue& valueSlot = m_storage->m_vector[length]; - result = valueSlot; - valueSlot = JSValue(); - if (result) + if (valueSlot) { --m_storage->m_numValuesInVector; - else + result = valueSlot; + valueSlot = JSValue(); + } else result = jsUndefined(); } else { result = jsUndefined(); @@ -573,11 +584,10 @@ void JSArray::push(ExecState* exec, JSValue value) { checkConsistency(); - if (m_storage->m_length < m_storage->m_vectorLength) { - ASSERT(!m_storage->m_vector[m_storage->m_length]); + if (m_storage->m_length < m_vectorLength) { m_storage->m_vector[m_storage->m_length] = value; - if (++m_storage->m_numValuesInVector == ++m_storage->m_length) - m_fastAccessCutoff = m_storage->m_length; + ++m_storage->m_numValuesInVector; + ++m_storage->m_length; checkConsistency(); return; } @@ -587,8 +597,8 @@ void JSArray::push(ExecState* exec, JSValue value) if (!map || map->isEmpty()) { if (increaseVectorLength(m_storage->m_length + 1)) { m_storage->m_vector[m_storage->m_length] = value; - if (++m_storage->m_numValuesInVector == ++m_storage->m_length) - m_fastAccessCutoff = m_storage->m_length; + ++m_storage->m_numValuesInVector; + ++m_storage->m_length; checkConsistency(); return; } @@ -603,18 +613,7 @@ void JSArray::push(ExecState* exec, JSValue value) void JSArray::markChildren(MarkStack& markStack) { - JSObject::markChildren(markStack); - - ArrayStorage* storage = m_storage; - - unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength); - markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues); - - if (SparseArrayValueMap* map = storage->m_sparseValueMap) { - SparseArrayValueMap::iterator end = map->end(); - for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) - markStack.append(it->second); - } + markChildrenDirect(markStack); } static int compareNumbersForQSort(const void* a, const void* b) @@ -817,7 +816,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, if (!m_storage->m_length) return; - unsigned usedVectorLength = min(m_storage->m_length, m_storage->m_vectorLength); + unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items tree.abstractor().m_exec = exec; @@ -866,7 +865,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { newUsedVectorLength += map->size(); - if (newUsedVectorLength > m_storage->m_vectorLength) { + if (newUsedVectorLength > m_vectorLength) { // Check that it is possible to allocate an array large enough to hold all the entries. if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) { throwOutOfMemoryError(exec); @@ -906,7 +905,6 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) m_storage->m_vector[i] = JSValue(); - m_fastAccessCutoff = newUsedVectorLength; m_storage->m_numValuesInVector = newUsedVectorLength; checkConsistency(SortConsistencyCheck); @@ -914,10 +912,16 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) { - unsigned fastAccessLength = min(m_storage->m_length, m_fastAccessCutoff); + JSValue* vector = m_storage->m_vector; + unsigned vectorEnd = min(m_storage->m_length, m_vectorLength); unsigned i = 0; - for (; i < fastAccessLength; ++i) - args.append(getIndex(i)); + for (; i < vectorEnd; ++i) { + JSValue& v = vector[i]; + if (!v) + break; + args.append(v); + } + for (; i < m_storage->m_length; ++i) args.append(get(exec, i)); } @@ -926,12 +930,17 @@ void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSiz { ASSERT(m_storage->m_length == maxSize); UNUSED_PARAM(maxSize); - unsigned fastAccessLength = min(m_storage->m_length, m_fastAccessCutoff); + JSValue* vector = m_storage->m_vector; + unsigned vectorEnd = min(m_storage->m_length, m_vectorLength); unsigned i = 0; - for (; i < fastAccessLength; ++i) - buffer[i] = getIndex(i); - uint32_t size = m_storage->m_length; - for (; i < size; ++i) + for (; i < vectorEnd; ++i) { + JSValue& v = vector[i]; + if (!v) + break; + buffer[i] = v; + } + + for (; i < m_storage->m_length; ++i) buffer[i] = get(exec, i); } @@ -941,7 +950,7 @@ unsigned JSArray::compactForSorting() ArrayStorage* storage = m_storage; - unsigned usedVectorLength = min(m_storage->m_length, storage->m_vectorLength); + unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); unsigned numDefined = 0; unsigned numUndefined = 0; @@ -965,7 +974,7 @@ unsigned JSArray::compactForSorting() if (SparseArrayValueMap* map = storage->m_sparseValueMap) { newUsedVectorLength += map->size(); - if (newUsedVectorLength > storage->m_vectorLength) { + if (newUsedVectorLength > m_vectorLength) { // Check that it is possible to allocate an array large enough to hold all the entries - if not, // exception is thrown by caller. if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) @@ -986,7 +995,6 @@ unsigned JSArray::compactForSorting() for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) storage->m_vector[i] = JSValue(); - m_fastAccessCutoff = newUsedVectorLength; storage->m_numValuesInVector = newUsedVectorLength; checkConsistency(SortConsistencyCheck); @@ -1012,30 +1020,27 @@ void JSArray::checkConsistency(ConsistencyCheckType type) if (type == SortConsistencyCheck) ASSERT(!m_storage->m_sparseValueMap); - ASSERT(m_fastAccessCutoff <= m_storage->m_length); - ASSERT(m_fastAccessCutoff <= m_storage->m_numValuesInVector); - unsigned numValuesInVector = 0; - for (unsigned i = 0; i < m_storage->m_vectorLength; ++i) { + for (unsigned i = 0; i < m_vectorLength; ++i) { if (JSValue value = m_storage->m_vector[i]) { ASSERT(i < m_storage->m_length); if (type != DestructorConsistencyCheck) value->type(); // Likely to crash if the object was deallocated. ++numValuesInVector; } else { - ASSERT(i >= m_fastAccessCutoff); if (type == SortConsistencyCheck) ASSERT(i >= m_storage->m_numValuesInVector); } } ASSERT(numValuesInVector == m_storage->m_numValuesInVector); + ASSERT(numValuesInVector <= m_storage->m_length); if (m_storage->m_sparseValueMap) { SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end(); for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) { unsigned index = it->first; ASSERT(index < m_storage->m_length); - ASSERT(index >= m_storage->m_vectorLength); + ASSERT(index >= m_vectorLength); ASSERT(index <= MAX_ARRAY_INDEX); ASSERT(it->second); if (type != DestructorConsistencyCheck) @@ -1046,26 +1051,4 @@ void JSArray::checkConsistency(ConsistencyCheckType type) #endif -JSArray* constructEmptyArray(ExecState* exec) -{ - return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure()); -} - -JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength) -{ - return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength); -} - -JSArray* constructArray(ExecState* exec, JSValue singleItemValue) -{ - MarkedArgumentBuffer values; - values.append(singleItemValue); - return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values); -} - -JSArray* constructArray(ExecState* exec, const ArgList& values) -{ - return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values); -} - } // namespace JSC diff --git a/JavaScriptCore/runtime/JSArray.h b/JavaScriptCore/runtime/JSArray.h index 49df6c4..66b5a1d 100644 --- a/JavaScriptCore/runtime/JSArray.h +++ b/JavaScriptCore/runtime/JSArray.h @@ -29,7 +29,6 @@ namespace JSC { struct ArrayStorage { unsigned m_length; - unsigned m_vectorLength; unsigned m_numValuesInVector; SparseArrayValueMap* m_sparseValueMap; void* lazyCreationData; // A JSArray subclass can use this to fill the vector lazily. @@ -38,15 +37,17 @@ namespace JSC { class JSArray : public JSObject { friend class JIT; + friend class Walker; public: - explicit JSArray(PassRefPtr<Structure>); - JSArray(PassRefPtr<Structure>, unsigned initialLength); - JSArray(PassRefPtr<Structure>, const ArgList& initialValues); + explicit JSArray(NonNullPassRefPtr<Structure>); + JSArray(NonNullPassRefPtr<Structure>, unsigned initialLength); + JSArray(NonNullPassRefPtr<Structure>, const ArgList& initialValues); virtual ~JSArray(); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState*, unsigned propertyName, JSValue); // FIXME: Make protected and add setItem. static JS_EXPORTDATA const ClassInfo info; @@ -61,18 +62,24 @@ namespace JSC { void push(ExecState*, JSValue); JSValue pop(); - bool canGetIndex(unsigned i) { return i < m_fastAccessCutoff; } + bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; } JSValue getIndex(unsigned i) { ASSERT(canGetIndex(i)); return m_storage->m_vector[i]; } - bool canSetIndex(unsigned i) { return i < m_fastAccessCutoff; } - JSValue setIndex(unsigned i, JSValue v) + bool canSetIndex(unsigned i) { return i < m_vectorLength; } + void setIndex(unsigned i, JSValue v) { ASSERT(canSetIndex(i)); - return m_storage->m_vector[i] = v; + JSValue& x = m_storage->m_vector[i]; + if (!x) { + ++m_storage->m_numValuesInVector; + if (i >= m_storage->m_length) + m_storage->m_length = i + 1; + } + x = v; } void fillArgList(ExecState*, MarkedArgumentBuffer&); @@ -82,12 +89,14 @@ namespace JSC { { return Structure::create(prototype, TypeInfo(ObjectType)); } + + inline void markChildrenDirect(MarkStack& markStack); protected: virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual bool deleteProperty(ExecState*, const Identifier& propertyName); virtual bool deleteProperty(ExecState*, unsigned propertyName); - virtual void getPropertyNames(ExecState*, PropertyNameArray&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&); virtual void markChildren(MarkStack&); void* lazyCreationData(); @@ -106,25 +115,110 @@ namespace JSC { enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck }; void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck); - unsigned m_fastAccessCutoff; + unsigned m_vectorLength; ArrayStorage* m_storage; }; JSArray* asArray(JSValue); - JSArray* constructEmptyArray(ExecState*); - JSArray* constructEmptyArray(ExecState*, unsigned initialLength); - JSArray* constructArray(ExecState*, JSValue singleItemValue); - JSArray* constructArray(ExecState*, const ArgList& values); + inline JSArray* asArray(JSCell* cell) + { + ASSERT(cell->inherits(&JSArray::info)); + return static_cast<JSArray*>(cell); + } inline JSArray* asArray(JSValue value) { - ASSERT(asObject(value)->inherits(&JSArray::info)); - return static_cast<JSArray*>(asObject(value)); + return asArray(value.asCell()); + } + + inline bool isJSArray(JSGlobalData* globalData, JSValue v) + { + return v.isCell() && v.asCell()->vptr() == globalData->jsArrayVPtr; + } + inline bool isJSArray(JSGlobalData* globalData, JSCell* cell) { return cell->vptr() == globalData->jsArrayVPtr; } + + inline void JSArray::markChildrenDirect(MarkStack& markStack) + { + JSObject::markChildrenDirect(markStack); + + ArrayStorage* storage = m_storage; + + unsigned usedVectorLength = std::min(storage->m_length, m_vectorLength); + markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues); + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) + markStack.append(it->second); + } } - inline bool isJSArray(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsArrayVPtr; } + inline void MarkStack::markChildren(JSCell* cell) + { + ASSERT(Heap::isCellMarked(cell)); + if (cell->structure()->typeInfo().hasDefaultMark()) { +#ifdef NDEBUG + asObject(cell)->markChildrenDirect(*this); +#else + ASSERT(!m_isCheckingForDefaultMarkViolation); + m_isCheckingForDefaultMarkViolation = true; + cell->markChildren(*this); + ASSERT(m_isCheckingForDefaultMarkViolation); + m_isCheckingForDefaultMarkViolation = false; +#endif + return; + } + if (cell->vptr() == m_jsArrayVPtr) { + asArray(cell)->markChildrenDirect(*this); + return; + } + cell->markChildren(*this); + } + inline void MarkStack::drain() + { + while (!m_markSets.isEmpty() || !m_values.isEmpty()) { + while (!m_markSets.isEmpty() && m_values.size() < 50) { + ASSERT(!m_markSets.isEmpty()); + MarkSet& current = m_markSets.last(); + ASSERT(current.m_values); + JSValue* end = current.m_end; + ASSERT(current.m_values); + ASSERT(current.m_values != end); + findNextUnmarkedNullValue: + ASSERT(current.m_values != end); + JSValue value = *current.m_values; + current.m_values++; + + JSCell* cell; + if (!value || !value.isCell() || Heap::isCellMarked(cell = value.asCell())) { + if (current.m_values == end) { + m_markSets.removeLast(); + continue; + } + goto findNextUnmarkedNullValue; + } + + Heap::markCell(cell); + if (cell->structure()->typeInfo().type() < CompoundType) { + if (current.m_values == end) { + m_markSets.removeLast(); + continue; + } + goto findNextUnmarkedNullValue; + } + + if (current.m_values == end) + m_markSets.removeLast(); + + markChildren(cell); + } + while (!m_values.isEmpty()) + markChildren(m_values.removeLast()); + } + } + } // namespace JSC #endif // JSArray_h diff --git a/JavaScriptCore/runtime/JSByteArray.cpp b/JavaScriptCore/runtime/JSByteArray.cpp index 2a5e72f..90d39f0 100644 --- a/JavaScriptCore/runtime/JSByteArray.cpp +++ b/JavaScriptCore/runtime/JSByteArray.cpp @@ -35,7 +35,7 @@ namespace JSC { const ClassInfo JSByteArray::s_defaultInfo = { "ByteArray", 0, 0, 0 }; -JSByteArray::JSByteArray(ExecState* exec, PassRefPtr<Structure> structure, ByteArray* storage, const JSC::ClassInfo* classInfo) +JSByteArray::JSByteArray(ExecState* exec, NonNullPassRefPtr<Structure> structure, ByteArray* storage, const JSC::ClassInfo* classInfo) : JSObject(structure) , m_storage(storage) , m_classInfo(classInfo) @@ -45,7 +45,7 @@ JSByteArray::JSByteArray(ExecState* exec, PassRefPtr<Structure> structure, ByteA PassRefPtr<Structure> JSByteArray::createStructure(JSValue prototype) { - PassRefPtr<Structure> result = Structure::create(prototype, TypeInfo(ObjectType)); + PassRefPtr<Structure> result = Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark)); return result; } @@ -59,7 +59,18 @@ bool JSByteArray::getOwnPropertySlot(ExecState* exec, const Identifier& property } return JSObject::getOwnPropertySlot(exec, propertyName, slot); } - + +bool JSByteArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + bool ok; + unsigned index = propertyName.toUInt32(&ok, false); + if (ok && canAccessIndex(index)) { + descriptor.setDescriptor(getIndex(exec, index), DontDelete); + return true; + } + return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + bool JSByteArray::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { if (canAccessIndex(propertyName)) { @@ -85,12 +96,12 @@ void JSByteArray::put(ExecState* exec, unsigned propertyName, JSValue value) setIndex(exec, propertyName, value); } -void JSByteArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +void JSByteArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) { unsigned length = m_storage->length(); for (unsigned i = 0; i < length; ++i) propertyNames.add(Identifier::from(exec, i)); - JSObject::getPropertyNames(exec, propertyNames); + JSObject::getOwnPropertyNames(exec, propertyNames); } } diff --git a/JavaScriptCore/runtime/JSByteArray.h b/JavaScriptCore/runtime/JSByteArray.h index a56aca6..006f4a2 100644 --- a/JavaScriptCore/runtime/JSByteArray.h +++ b/JavaScriptCore/runtime/JSByteArray.h @@ -73,15 +73,16 @@ namespace JSC { setIndex(i, byteValue); } - JSByteArray(ExecState* exec, PassRefPtr<Structure>, WTF::ByteArray* storage, const JSC::ClassInfo* = &s_defaultInfo); + JSByteArray(ExecState* exec, NonNullPassRefPtr<Structure>, WTF::ByteArray* storage, const JSC::ClassInfo* = &s_defaultInfo); static PassRefPtr<Structure> createStructure(JSValue prototype); virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); virtual bool getOwnPropertySlot(JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&); virtual void put(JSC::ExecState*, unsigned propertyName, JSC::JSValue); - virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&); + virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&); virtual const ClassInfo* classInfo() const { return m_classInfo; } static const ClassInfo s_defaultInfo; diff --git a/JavaScriptCore/runtime/JSCell.cpp b/JavaScriptCore/runtime/JSCell.cpp index c733ed9..aa93252 100644 --- a/JavaScriptCore/runtime/JSCell.cpp +++ b/JavaScriptCore/runtime/JSCell.cpp @@ -105,7 +105,7 @@ UString JSCell::getString() const JSObject* JSCell::getObject() { - return isObject() ? static_cast<JSObject*>(this) : 0; + return isObject() ? asObject(this) : 0; } const JSObject* JSCell::getObject() const @@ -197,4 +197,40 @@ bool JSCell::isGetterSetter() const return false; } +JSValue JSCell::toPrimitive(ExecState*, PreferredPrimitiveType) const +{ + ASSERT_NOT_REACHED(); + return JSValue(); +} + +bool JSCell::getPrimitiveNumber(ExecState*, double&, JSValue&) +{ + ASSERT_NOT_REACHED(); + return false; +} + +bool JSCell::toBoolean(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return false; +} + +double JSCell::toNumber(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +UString JSCell::toString(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return UString(); +} + +JSObject* JSCell::toObject(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return 0; +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h index 75ccf7f..503c6c4 100644 --- a/JavaScriptCore/runtime/JSCell.h +++ b/JavaScriptCore/runtime/JSCell.h @@ -23,11 +23,12 @@ #ifndef JSCell_h #define JSCell_h -#include <wtf/Noncopyable.h> -#include "Structure.h" -#include "JSValue.h" -#include "JSImmediate.h" #include "Collector.h" +#include "JSImmediate.h" +#include "JSValue.h" +#include "MarkStack.h" +#include "Structure.h" +#include <wtf/Noncopyable.h> namespace JSC { @@ -45,6 +46,7 @@ namespace JSC { private: explicit JSCell(Structure*); + JSCell(); // Only used for initializing Collector blocks. virtual ~JSCell(); public: @@ -55,7 +57,7 @@ namespace JSC { bool isString() const; bool isObject() const; virtual bool isGetterSetter() const; - virtual bool isObject(const ClassInfo*) const; + bool inherits(const ClassInfo*) const; virtual bool isAPIValueWrapper() const { return false; } Structure* structure() const; @@ -74,21 +76,19 @@ namespace JSC { virtual bool getUInt32(uint32_t&) const; // Basic conversions. - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const = 0; - virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue&) = 0; - virtual bool toBoolean(ExecState*) const = 0; - virtual double toNumber(ExecState*) const = 0; - virtual UString toString(ExecState*) const = 0; - virtual JSObject* toObject(ExecState*) const = 0; + virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue&); + virtual bool toBoolean(ExecState*) const; + virtual double toNumber(ExecState*) const; + virtual UString toString(ExecState*) const; + virtual JSObject* toObject(ExecState*) const; // Garbage collection. void* operator new(size_t, ExecState*); void* operator new(size_t, JSGlobalData*); void* operator new(size_t, void* placementNewDestination) { return placementNewDestination; } - void markCellDirect(); virtual void markChildren(MarkStack&); - bool marked() const; // Object operations, with the toObject operation included. virtual const ClassInfo* classInfo() const; @@ -112,6 +112,7 @@ namespace JSC { Structure* m_structure; }; + // FIXME: We should deprecate this and just use JSValue::asCell() instead. JSCell* asCell(JSValue); inline JSCell* asCell(JSValue value) @@ -124,6 +125,11 @@ namespace JSC { { } + // Only used for initializing Collector blocks. + inline JSCell::JSCell() + { + } + inline JSCell::~JSCell() { } @@ -150,19 +156,8 @@ namespace JSC { return m_structure; } - inline bool JSCell::marked() const - { - return Heap::isCellMarked(this); - } - - inline void JSCell::markCellDirect() - { - Heap::markCell(this); - } - inline void JSCell::markChildren(MarkStack&) { - ASSERT(marked()); } inline void* JSCell::operator new(size_t size, JSGlobalData* globalData) @@ -231,23 +226,6 @@ namespace JSC { return false; } - inline void JSValue::markDirect() - { - ASSERT(!marked()); - asCell()->markCellDirect(); - } - - inline void JSValue::markChildren(MarkStack& markStack) - { - ASSERT(marked()); - asCell()->markChildren(markStack); - } - - inline bool JSValue::marked() const - { - return !isCell() || asCell()->marked(); - } - #if !USE(JSVALUE32_64) ALWAYS_INLINE JSCell* JSValue::asCell() const { @@ -315,24 +293,6 @@ namespace JSC { return isUndefined() ? nonInlineNaN() : 0; // null and false both convert to 0. } - inline UString JSValue::toString(ExecState* exec) const - { - if (isCell()) - return asCell()->toString(exec); - if (isInt32()) - return UString::from(asInt32()); - if (isDouble()) - return asDouble() == 0.0 ? "0" : UString::from(asDouble()); - if (isTrue()) - return "true"; - if (isFalse()) - return "false"; - if (isNull()) - return "null"; - ASSERT(isUndefined()); - return "undefined"; - } - inline bool JSValue::needsThisConversion() const { if (UNLIKELY(!isCell())) @@ -353,12 +313,6 @@ namespace JSC { return asCell()->getJSNumber(); return JSValue(); } - - inline bool JSValue::hasChildren() const - { - return asCell()->structure()->typeInfo().type() >= CompoundType; - } - inline JSObject* JSValue::toObject(ExecState* exec) const { @@ -372,37 +326,39 @@ namespace JSC { ALWAYS_INLINE void MarkStack::append(JSCell* cell) { + ASSERT(!m_isCheckingForDefaultMarkViolation); ASSERT(cell); - if (cell->marked()) + if (Heap::isCellMarked(cell)) return; - cell->markCellDirect(); + Heap::markCell(cell); if (cell->structure()->typeInfo().type() >= CompoundType) m_values.append(cell); } - inline void MarkStack::drain() { - while (!m_markSets.isEmpty() || !m_values.isEmpty()) { - while ((!m_markSets.isEmpty()) && m_values.size() < 50) { - const MarkSet& current = m_markSets.removeLast(); - JSValue* ptr = current.m_values; - JSValue* end = current.m_end; - if (current.m_properties == NoNullValues) { - while (ptr != end) - append(*ptr++); - } else { - while (ptr != end) { - if (JSValue value = *ptr++) - append(value); - } - } - } - while (!m_values.isEmpty()) { - JSCell* current = m_values.removeLast(); - ASSERT(current->marked()); - current->markChildren(*this); - } - } + ALWAYS_INLINE void MarkStack::append(JSValue value) + { + ASSERT(value); + if (value.isCell()) + append(value.asCell()); + } + + inline void Structure::markAggregate(MarkStack& markStack) + { + markStack.append(m_prototype); + } + + inline Heap* Heap::heap(JSValue v) + { + if (!v.isCell()) + return 0; + return heap(v.asCell()); + } + + inline Heap* Heap::heap(JSCell* c) + { + return cellBlock(c)->heap; } + } // namespace JSC #endif // JSCell_h diff --git a/JavaScriptCore/runtime/JSFunction.cpp b/JavaScriptCore/runtime/JSFunction.cpp index 84c6263..024e586 100644 --- a/JavaScriptCore/runtime/JSFunction.cpp +++ b/JavaScriptCore/runtime/JSFunction.cpp @@ -45,12 +45,21 @@ ASSERT_CLASS_FITS_IN_CELL(JSFunction); const ClassInfo JSFunction::info = { "Function", &InternalFunction::info, 0, 0 }; -JSFunction::JSFunction(ExecState* exec, PassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func) +bool JSFunction::isHostFunctionNonInline() const +{ + return isHostFunction(); +} + +JSFunction::JSFunction(NonNullPassRefPtr<Structure> structure) + : Base(structure) + , m_executable(adoptRef(new VPtrHackExecutable())) +{ +} + +JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func) : Base(&exec->globalData(), structure, name) #if ENABLE(JIT) - , m_body(FunctionBodyNode::createNativeThunk(&exec->globalData())) -#else - , m_body(0) + , m_executable(adoptRef(new NativeExecutable(exec))) #endif { #if ENABLE(JIT) @@ -63,9 +72,9 @@ JSFunction::JSFunction(ExecState* exec, PassRefPtr<Structure> structure, int len #endif } -JSFunction::JSFunction(ExecState* exec, const Identifier& name, FunctionBodyNode* body, ScopeChainNode* scopeChainNode) - : Base(&exec->globalData(), exec->lexicalGlobalObject()->functionStructure(), name) - , m_body(body) +JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<FunctionExecutable> executable, ScopeChainNode* scopeChainNode) + : Base(&exec->globalData(), exec->lexicalGlobalObject()->functionStructure(), executable->name()) + , m_executable(executable) { setScopeChain(scopeChainNode); } @@ -75,20 +84,23 @@ JSFunction::~JSFunction() // JIT code for other functions may have had calls linked directly to the code for this function; these links // are based on a check for the this pointer value for this JSFunction - which will no longer be valid once // this memory is freed and may be reused (potentially for another, different JSFunction). + if (!isHostFunction()) { #if ENABLE(JIT_OPTIMIZE_CALL) - if (m_body && m_body->isGenerated()) - m_body->generatedBytecode().unlinkCallers(); + ASSERT(m_executable); + if (jsExecutable()->isGenerated()) + jsExecutable()->generatedBytecode().unlinkCallers(); #endif - if (!isHostFunction()) scopeChain().~ScopeChain(); // FIXME: Don't we need to do this in the interpreter too? + } } void JSFunction::markChildren(MarkStack& markStack) { Base::markChildren(markStack); - m_body->markAggregate(markStack); - if (!isHostFunction()) + if (!isHostFunction()) { + jsExecutable()->markAggregate(markStack); scopeChain().markAggregate(markStack); + } } CallType JSFunction::getCallData(CallData& callData) @@ -97,7 +109,7 @@ CallType JSFunction::getCallData(CallData& callData) callData.native.function = nativeFunction(); return CallTypeHost; } - callData.js.functionBody = m_body.get(); + callData.js.functionExecutable = jsExecutable(); callData.js.scopeChain = scopeChain().node(); return CallTypeJS; } @@ -105,7 +117,7 @@ CallType JSFunction::getCallData(CallData& callData) JSValue JSFunction::call(ExecState* exec, JSValue thisValue, const ArgList& args) { ASSERT(!isHostFunction()); - return exec->interpreter()->execute(m_body.get(), exec, this, thisValue.toThisObject(exec), args, scopeChain().node(), exec->exceptionSlot()); + return exec->interpreter()->execute(jsExecutable(), exec, this, thisValue.toThisObject(exec), args, scopeChain().node(), exec->exceptionSlot()); } JSValue JSFunction::argumentsGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) @@ -126,7 +138,7 @@ JSValue JSFunction::lengthGetter(ExecState* exec, const Identifier&, const Prope { JSFunction* thisObj = asFunction(slot.slotBase()); ASSERT(!thisObj->isHostFunction()); - return jsNumber(exec, thisObj->m_body->parameterCount()); + return jsNumber(exec, thisObj->jsExecutable()->parameterCount()); } bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -165,6 +177,35 @@ bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyN return Base::getOwnPropertySlot(exec, propertyName, slot); } + bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + if (isHostFunction()) + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); + + if (propertyName == exec->propertyNames().prototype) { + PropertySlot slot; + getOwnPropertySlot(exec, propertyName, slot); + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); + } + + if (propertyName == exec->propertyNames().arguments) { + descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(exec, jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete); + return true; + } + + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); + } + void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { if (isHostFunction()) { @@ -190,7 +231,7 @@ ConstructType JSFunction::getConstructData(ConstructData& constructData) { if (isHostFunction()) return ConstructTypeNone; - constructData.js.functionBody = m_body.get(); + constructData.js.functionExecutable = jsExecutable(); constructData.js.scopeChain = scopeChain().node(); return ConstructTypeJS; } @@ -206,7 +247,7 @@ JSObject* JSFunction::construct(ExecState* exec, const ArgList& args) structure = exec->lexicalGlobalObject()->emptyObjectStructure(); JSObject* thisObj = new (exec) JSObject(structure); - JSValue result = exec->interpreter()->execute(m_body.get(), exec, this, thisObj, args, scopeChain().node(), exec->exceptionSlot()); + JSValue result = exec->interpreter()->execute(jsExecutable(), exec, this, thisObj, args, scopeChain().node(), exec->exceptionSlot()); if (exec->hadException() || !result.isObject()) return thisObj; return asObject(result); diff --git a/JavaScriptCore/runtime/JSFunction.h b/JavaScriptCore/runtime/JSFunction.h index cab1e5b..a9ac63e 100644 --- a/JavaScriptCore/runtime/JSFunction.h +++ b/JavaScriptCore/runtime/JSFunction.h @@ -25,14 +25,11 @@ #define JSFunction_h #include "InternalFunction.h" -#include "JSVariableObject.h" -#include "SymbolTable.h" -#include "Nodes.h" -#include "JSObject.h" namespace JSC { - class FunctionBodyNode; + class ExecutableBase; + class FunctionExecutable; class FunctionPrototype; class JSActivation; class JSGlobalObject; @@ -43,20 +40,10 @@ namespace JSC { typedef InternalFunction Base; - JSFunction(PassRefPtr<Structure> structure) - : InternalFunction(structure) - { - clearScopeChain(); - } - public: - JSFunction(ExecState*, PassRefPtr<Structure>, int length, const Identifier&, NativeFunction); - JSFunction(ExecState*, const Identifier&, FunctionBodyNode*, ScopeChainNode*); - ~JSFunction(); - - virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); - virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); - virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + JSFunction(ExecState*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); + JSFunction(ExecState*, NonNullPassRefPtr<FunctionExecutable>, ScopeChainNode*); + virtual ~JSFunction(); JSObject* construct(ExecState*, const ArgList&); JSValue call(ExecState*, JSValue thisValue, const ArgList&); @@ -64,11 +51,11 @@ namespace JSC { void setScope(const ScopeChain& scopeChain) { setScopeChain(scopeChain); } ScopeChain& scope() { return scopeChain(); } - void setBody(FunctionBodyNode* body) { m_body = body; } - void setBody(PassRefPtr<FunctionBodyNode> body) { m_body = body; } - FunctionBodyNode* body() const { return m_body.get(); } + ExecutableBase* executable() const { return m_executable.get(); } - virtual void markChildren(MarkStack&); + // To call either of these methods include Executable.h + inline bool isHostFunction() const; + FunctionExecutable* jsExecutable() const; static JS_EXPORTDATA const ClassInfo info; @@ -77,11 +64,6 @@ namespace JSC { return Structure::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance)); } -#if ENABLE(JIT) - bool isHostFunction() const { return m_body && m_body->isHostFunction(); } -#else - bool isHostFunction() const { return false; } -#endif NativeFunction nativeFunction() { return *reinterpret_cast<NativeFunction*>(m_data); @@ -91,31 +73,42 @@ namespace JSC { virtual CallType getCallData(CallData&); private: + JSFunction(NonNullPassRefPtr<Structure>); + + bool isHostFunctionNonInline() const; + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + + virtual void markChildren(MarkStack&); + virtual const ClassInfo* classInfo() const { return &info; } static JSValue argumentsGetter(ExecState*, const Identifier&, const PropertySlot&); static JSValue callerGetter(ExecState*, const Identifier&, const PropertySlot&); static JSValue lengthGetter(ExecState*, const Identifier&, const PropertySlot&); - RefPtr<FunctionBodyNode> m_body; + RefPtr<ExecutableBase> m_executable; ScopeChain& scopeChain() { - ASSERT(!isHostFunction()); + ASSERT(!isHostFunctionNonInline()); return *reinterpret_cast<ScopeChain*>(m_data); } void clearScopeChain() { - ASSERT(!isHostFunction()); + ASSERT(!isHostFunctionNonInline()); new (m_data) ScopeChain(NoScopeChain()); } void setScopeChain(ScopeChainNode* sc) { - ASSERT(!isHostFunction()); + ASSERT(!isHostFunctionNonInline()); new (m_data) ScopeChain(sc); } void setScopeChain(const ScopeChain& sc) { - ASSERT(!isHostFunction()); + ASSERT(!isHostFunctionNonInline()); *reinterpret_cast<ScopeChain*>(m_data) = sc; } void setNativeFunction(NativeFunction func) diff --git a/JavaScriptCore/runtime/JSGlobalData.cpp b/JavaScriptCore/runtime/JSGlobalData.cpp index 03df41d..1221ef2 100644 --- a/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/JavaScriptCore/runtime/JSGlobalData.cpp @@ -144,8 +144,12 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) , initializingLazyNumericCompareFunction(false) , head(0) , dynamicGlobalObject(0) - , scopeNodeBeingReparsed(0) + , functionCodeBlockBeingReparsed(0) , firstStringifierToMark(0) + , markStack(vptrSet.jsArrayVPtr) +#ifndef NDEBUG + , mainThreadOnly(false) +#endif { #if PLATFORM(MAC) startProfilerServerIfNeeded(); @@ -235,9 +239,8 @@ const Vector<Instruction>& JSGlobalData::numericCompareFunction(ExecState* exec) { if (!lazyNumericCompareFunction.size() && !initializingLazyNumericCompareFunction) { initializingLazyNumericCompareFunction = true; - RefPtr<ProgramNode> programNode = parser->parse<ProgramNode>(exec, 0, makeSource(UString("(function (v1, v2) { return v1 - v2; })")), 0, 0); - RefPtr<FunctionBodyNode> functionBody = extractFunctionBody(programNode.get()); - lazyNumericCompareFunction = functionBody->bytecode(exec->scopeChain()).instructions(); + RefPtr<FunctionExecutable> function = FunctionExecutable::fromGlobalCode(Identifier(exec, "numericCompare"), exec, 0, makeSource(UString("(function (v1, v2) { return v1 - v2; })")), 0, 0); + lazyNumericCompareFunction = function->bytecode(exec, exec->scopeChain()).instructions(); initializingLazyNumericCompareFunction = false; } @@ -248,4 +251,19 @@ JSGlobalData::ClientData::~ClientData() { } +void JSGlobalData::startSampling() +{ + interpreter->startSampling(); +} + +void JSGlobalData::stopSampling() +{ + interpreter->stopSampling(); +} + +void JSGlobalData::dumpSampleData(ExecState* exec) +{ + interpreter->dumpSampleData(exec); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSGlobalData.h b/JavaScriptCore/runtime/JSGlobalData.h index 88cb516..3ad90ad 100644 --- a/JavaScriptCore/runtime/JSGlobalData.h +++ b/JavaScriptCore/runtime/JSGlobalData.h @@ -34,6 +34,7 @@ #include "JITStubs.h" #include "JSValue.h" #include "MarkStack.h" +#include "NumericStrings.h" #include "SmallStrings.h" #include "TimeoutChecker.h" #include <wtf/Forward.h> @@ -45,15 +46,14 @@ struct OpaqueJSClassContextData; namespace JSC { + class CodeBlock; class CommonIdentifiers; - class FunctionBodyNode; class IdentifierTable; class Interpreter; class JSGlobalObject; class JSObject; class Lexer; class Parser; - class ScopeNode; class Stringifier; class Structure; class UString; @@ -115,6 +115,7 @@ namespace JSC { CommonIdentifiers* propertyNames; const MarkedArgumentBuffer* emptyList; // Lists are supposed to be allocated on the stack to have their elements properly marked, which is not the case here - but this list has nothing to mark. SmallStrings smallStrings; + NumericStrings numericStrings; #if ENABLE(ASSEMBLER) ExecutableAllocator executableAllocator; @@ -145,10 +146,18 @@ namespace JSC { HashSet<JSObject*> arrayVisitedElements; - ScopeNode* scopeNodeBeingReparsed; + CodeBlock* functionCodeBlockBeingReparsed; Stringifier* firstStringifierToMark; MarkStack markStack; + +#ifndef NDEBUG + bool mainThreadOnly; +#endif + + void startSampling(); + void stopSampling(); + void dumpSampleData(ExecState* exec); private: JSGlobalData(bool isShared, const VPtrSet&); static JSGlobalData*& sharedInstanceInternal(); diff --git a/JavaScriptCore/runtime/JSGlobalObject.cpp b/JavaScriptCore/runtime/JSGlobalObject.cpp index a90f18f..3bb281e 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -112,8 +112,8 @@ JSGlobalObject::~JSGlobalObject() if (headObject == this) headObject = 0; - HashSet<ProgramCodeBlock*>::const_iterator end = codeBlocks().end(); - for (HashSet<ProgramCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) + HashSet<GlobalCodeBlock*>::const_iterator end = codeBlocks().end(); + for (HashSet<GlobalCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) (*it)->clearGlobalObject(); RegisterFile& registerFile = globalData()->interpreter->registerFile(); @@ -121,7 +121,7 @@ JSGlobalObject::~JSGlobalObject() registerFile.setGlobalObject(0); registerFile.setNumGlobals(0); } - delete d(); + d()->destructor(d()); } void JSGlobalObject::init(JSObject* thisValue) @@ -129,7 +129,7 @@ void JSGlobalObject::init(JSObject* thisValue) ASSERT(JSLock::currentThreadIsHoldingLock()); d()->globalData = Heap::heap(this)->globalData(); - d()->globalScopeChain = ScopeChain(this, d()->globalData.get(), thisValue); + d()->globalScopeChain = ScopeChain(this, d()->globalData.get(), this, thisValue); JSGlobalObject::globalExec()->init(0, 0, d()->globalScopeChain.node(), CallFrame::noCaller(), 0, 0, 0); @@ -175,18 +175,18 @@ void JSGlobalObject::putWithAttributes(ExecState* exec, const Identifier& proper } } -void JSGlobalObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc) +void JSGlobalObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes) { PropertySlot slot; if (!symbolTableGet(propertyName, slot)) - JSVariableObject::defineGetter(exec, propertyName, getterFunc); + JSVariableObject::defineGetter(exec, propertyName, getterFunc, attributes); } -void JSGlobalObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc) +void JSGlobalObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes) { PropertySlot slot; if (!symbolTableGet(propertyName, slot)) - JSVariableObject::defineSetter(exec, propertyName, setterFunc); + JSVariableObject::defineSetter(exec, propertyName, setterFunc, attributes); } static inline JSObject* lastInPrototypeChain(JSObject* object) @@ -258,7 +258,7 @@ void JSGlobalObject::reset(JSValue prototype) JSCell* objectConstructor = new (exec) ObjectConstructor(exec, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype, d()->prototypeFunctionStructure.get()); JSCell* functionConstructor = new (exec) FunctionConstructor(exec, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype); - JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype); + JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype, d()->prototypeFunctionStructure.get()); JSCell* stringConstructor = new (exec) StringConstructor(exec, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype); JSCell* booleanConstructor = new (exec) BooleanConstructor(exec, BooleanConstructor::createStructure(d()->functionPrototype), d()->booleanPrototype); JSCell* numberConstructor = new (exec) NumberConstructor(exec, NumberConstructor::createStructure(d()->functionPrototype), d()->numberPrototype); @@ -361,8 +361,8 @@ void JSGlobalObject::markChildren(MarkStack& markStack) { JSVariableObject::markChildren(markStack); - HashSet<ProgramCodeBlock*>::const_iterator end = codeBlocks().end(); - for (HashSet<ProgramCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) + HashSet<GlobalCodeBlock*>::const_iterator end = codeBlocks().end(); + for (HashSet<GlobalCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) (*it)->markAggregate(markStack); RegisterFile& registerFile = globalData()->interpreter->registerFile(); @@ -455,4 +455,9 @@ void* JSGlobalObject::operator new(size_t size, JSGlobalData* globalData) #endif } +void JSGlobalObject::destroyJSGlobalObjectData(void* jsGlobalObjectData) +{ + delete static_cast<JSGlobalObjectData*>(jsGlobalObjectData); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSGlobalObject.h b/JavaScriptCore/runtime/JSGlobalObject.h index cda49bd..2106783 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.h +++ b/JavaScriptCore/runtime/JSGlobalObject.h @@ -22,6 +22,7 @@ #ifndef JSGlobalObject_h #define JSGlobalObject_h +#include "JSArray.h" #include "JSGlobalData.h" #include "JSVariableObject.h" #include "NativeFunctionWrapper.h" @@ -38,6 +39,7 @@ namespace JSC { class Debugger; class ErrorConstructor; class FunctionPrototype; + class GlobalCodeBlock; class GlobalEvalFunction; class NativeErrorConstructor; class ProgramCodeBlock; @@ -50,14 +52,22 @@ namespace JSC { struct HashTable; typedef Vector<ExecState*, 16> ExecStateStack; - + class JSGlobalObject : public JSVariableObject { protected: using JSVariableObject::JSVariableObjectData; struct JSGlobalObjectData : public JSVariableObjectData { - JSGlobalObjectData() + // We use an explicit destructor function pointer instead of a + // virtual destructor because we want to avoid adding a vtable + // pointer to this struct. Adding a vtable pointer would force the + // compiler to emit costly pointer fixup code when casting from + // JSVariableObjectData* to JSGlobalObjectData*. + typedef void (*Destructor)(void*); + + JSGlobalObjectData(Destructor destructor) : JSVariableObjectData(&symbolTable, 0) + , destructor(destructor) , registerArraySize(0) , globalScopeChain(NoScopeChain()) , regExpConstructor(0) @@ -83,10 +93,8 @@ namespace JSC { { } - virtual ~JSGlobalObjectData() - { - } - + Destructor destructor; + size_t registerArraySize; JSGlobalObject* next; @@ -144,20 +152,20 @@ namespace JSC { RefPtr<JSGlobalData> globalData; - HashSet<ProgramCodeBlock*> codeBlocks; + HashSet<GlobalCodeBlock*> codeBlocks; }; public: void* operator new(size_t, JSGlobalData*); explicit JSGlobalObject() - : JSVariableObject(JSGlobalObject::createStructure(jsNull()), new JSGlobalObjectData) + : JSVariableObject(JSGlobalObject::createStructure(jsNull()), new JSGlobalObjectData(destroyJSGlobalObjectData)) { init(this); } protected: - JSGlobalObject(PassRefPtr<Structure> structure, JSGlobalObjectData* data, JSObject* thisValue) + JSGlobalObject(NonNullPassRefPtr<Structure> structure, JSGlobalObjectData* data, JSObject* thisValue) : JSVariableObject(structure, data) { init(thisValue); @@ -169,12 +177,13 @@ namespace JSC { virtual void markChildren(MarkStack&); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual bool hasOwnPropertyForWrite(ExecState*, const Identifier&); virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); - virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc); - virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc); + virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes); + virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes); // Linked list of all global objects that use the same JSGlobalData. JSGlobalObject*& head() { return d()->globalData->head; } @@ -246,7 +255,7 @@ namespace JSC { virtual bool isDynamicScope() const; - HashSet<ProgramCodeBlock*>& codeBlocks() { return d()->codeBlocks; } + HashSet<GlobalCodeBlock*>& codeBlocks() { return d()->codeBlocks; } void copyGlobalsFrom(RegisterFile&); void copyGlobalsTo(RegisterFile&); @@ -277,6 +286,8 @@ namespace JSC { void addStaticGlobals(GlobalPropertyInfo*, int count); private: + static void destroyJSGlobalObjectData(void*); + // FIXME: Fold reset into init. void init(JSObject* thisValue); void reset(JSValue prototype); @@ -325,6 +336,13 @@ namespace JSC { return symbolTableGet(propertyName, slot); } + inline bool JSGlobalObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + if (symbolTableGet(propertyName, descriptor)) + return true; + return JSVariableObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); + } + inline bool JSGlobalObject::hasOwnPropertyForWrite(ExecState* exec, const Identifier& propertyName) { PropertySlot slot; @@ -334,14 +352,6 @@ namespace JSC { return symbolTableGet(propertyName, slot, slotIsWriteable); } - inline JSGlobalObject* ScopeChainNode::globalObject() const - { - const ScopeChainNode* n = this; - while (n->next) - n = n->next; - return asGlobalObject(n->object); - } - inline JSValue Structure::prototypeForLookup(ExecState* exec) const { if (typeInfo().type() == ObjectType) @@ -396,6 +406,33 @@ namespace JSC { return globalData().dynamicGlobalObject; } + inline JSObject* constructEmptyObject(ExecState* exec) + { + return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); + } + + inline JSArray* constructEmptyArray(ExecState* exec) + { + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure()); + } + + inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength) + { + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength); + } + + inline JSArray* constructArray(ExecState* exec, JSValue singleItemValue) + { + MarkedArgumentBuffer values; + values.append(singleItemValue); + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values); + } + + inline JSArray* constructArray(ExecState* exec, const ArgList& values) + { + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values); + } + class DynamicGlobalObjectScope : public Noncopyable { public: DynamicGlobalObjectScope(CallFrame* callFrame, JSGlobalObject* dynamicGlobalObject) diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index affb99c..dc32718 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -286,16 +286,12 @@ JSValue JSC_HOST_CALL globalFuncEval(ExecState* exec, JSObject* function, JSValu if (JSValue parsedObject = preparser.tryLiteralParse()) return parsedObject; - int errLine; - UString errMsg; + RefPtr<EvalExecutable> eval = EvalExecutable::create(exec, makeSource(s)); + JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node()); + if (error) + return throwError(exec, error); - SourceCode source = makeSource(s); - RefPtr<EvalNode> evalNode = exec->globalData().parser->parse<EvalNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); - - if (!evalNode) - return throwError(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), NULL); - - return exec->interpreter()->execute(evalNode.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot()); + return exec->interpreter()->execute(eval.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot()); } JSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec, JSObject*, JSValue, const ArgList& args) diff --git a/JavaScriptCore/runtime/JSNotAnObject.cpp b/JavaScriptCore/runtime/JSNotAnObject.cpp index a542a9f..c36dc10 100644 --- a/JavaScriptCore/runtime/JSNotAnObject.cpp +++ b/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -93,6 +93,12 @@ bool JSNotAnObject::getOwnPropertySlot(ExecState* exec, unsigned, PropertySlot&) return false; } +bool JSNotAnObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier&, PropertyDescriptor&) +{ + ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + return false; +} + void JSNotAnObject::put(ExecState* exec, const Identifier& , JSValue, PutPropertySlot&) { ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); @@ -115,7 +121,7 @@ bool JSNotAnObject::deleteProperty(ExecState* exec, unsigned) return false; } -void JSNotAnObject::getPropertyNames(ExecState* exec, PropertyNameArray&) +void JSNotAnObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray&) { ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); } diff --git a/JavaScriptCore/runtime/JSNotAnObject.h b/JavaScriptCore/runtime/JSNotAnObject.h index b65ff5f..0d9aca6 100644 --- a/JavaScriptCore/runtime/JSNotAnObject.h +++ b/JavaScriptCore/runtime/JSNotAnObject.h @@ -80,6 +80,7 @@ namespace JSC { // JSObject methods virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual void put(ExecState*, unsigned propertyName, JSValue); @@ -87,7 +88,7 @@ namespace JSC { virtual bool deleteProperty(ExecState*, const Identifier& propertyName); virtual bool deleteProperty(ExecState*, unsigned propertyName); - virtual void getPropertyNames(ExecState*, PropertyNameArray&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&); JSNotAnObjectErrorStub* m_exception; }; diff --git a/JavaScriptCore/runtime/JSNumberCell.cpp b/JavaScriptCore/runtime/JSNumberCell.cpp index 0654da7..f1009b9 100644 --- a/JavaScriptCore/runtime/JSNumberCell.cpp +++ b/JavaScriptCore/runtime/JSNumberCell.cpp @@ -54,15 +54,11 @@ double JSNumberCell::toNumber(ExecState*) const UString JSNumberCell::toString(ExecState*) const { - if (m_value == 0.0) // +0.0 or -0.0 - return "0"; return UString::from(m_value); } UString JSNumberCell::toThisString(ExecState*) const { - if (m_value == 0.0) // +0.0 or -0.0 - return "0"; return UString::from(m_value); } diff --git a/JavaScriptCore/runtime/JSNumberCell.h b/JavaScriptCore/runtime/JSNumberCell.h index 04cccef..6a48081 100644 --- a/JavaScriptCore/runtime/JSNumberCell.h +++ b/JavaScriptCore/runtime/JSNumberCell.h @@ -84,7 +84,7 @@ namespace JSC { #endif } - static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(NumberType, NeedsThisConversion)); } + static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(NumberType, NeedsThisConversion | HasDefaultMark)); } private: JSNumberCell(JSGlobalData* globalData, double value) diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp index d643808..297d457 100644 --- a/JavaScriptCore/runtime/JSONObject.cpp +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -120,38 +120,47 @@ private: // ------------------------------ helper functions -------------------------------- -static inline JSValue unwrapBoxedPrimitive(JSValue value) +static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value) { if (!value.isObject()) return value; - if (!asObject(value)->inherits(&NumberObject::info) && !asObject(value)->inherits(&StringObject::info) && !asObject(value)->inherits(&BooleanObject::info)) - return value; - return static_cast<JSWrapperObject*>(asObject(value))->internalValue(); + JSObject* object = asObject(value); + if (object->inherits(&NumberObject::info)) + return jsNumber(exec, object->toNumber(exec)); + if (object->inherits(&StringObject::info)) + return jsString(exec, object->toString(exec)); + if (object->inherits(&BooleanObject::info)) + return object->toPrimitive(exec); + return value; } -static inline UString gap(JSValue space) +static inline UString gap(ExecState* exec, JSValue space) { - space = unwrapBoxedPrimitive(space); + const int maxGapLength = 10; + space = unwrapBoxedPrimitive(exec, space); // If the space value is a number, create a gap string with that number of spaces. double spaceCount; if (space.getNumber(spaceCount)) { - const int maxSpaceCount = 100; int count; - if (spaceCount > maxSpaceCount) - count = maxSpaceCount; + if (spaceCount > maxGapLength) + count = maxGapLength; else if (!(spaceCount > 0)) count = 0; else count = static_cast<int>(spaceCount); - UChar spaces[maxSpaceCount]; + UChar spaces[maxGapLength]; for (int i = 0; i < count; ++i) spaces[i] = ' '; return UString(spaces, count); } // If the space value is a string, use it as the gap string, otherwise use no gap string. - return space.getString(); + UString spaces = space.getString(); + if (spaces.size() > maxGapLength) { + spaces = spaces.substr(0, maxGapLength); + } + return spaces; } // ------------------------------ PropertyNameForFunctionCall -------------------------------- @@ -187,7 +196,7 @@ Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space) , m_usingArrayReplacer(false) , m_arrayReplacerPropertyNames(exec) , m_replacerCallType(CallTypeNone) - , m_gap(gap(space)) + , m_gap(gap(exec, space)) { exec->globalData().firstStringifierToMark = this; @@ -202,12 +211,27 @@ Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space) JSValue name = array->get(exec, i); if (exec->hadException()) break; + UString propertyName; - if (!name.getString(propertyName)) + if (name.getString(propertyName)) { + m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); continue; - if (exec->hadException()) - return; - m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); + } + + double value = 0; + if (name.getNumber(value)) { + m_arrayReplacerPropertyNames.add(Identifier::from(exec, value)); + continue; + } + + if (name.isObject()) { + if (!asObject(name)->inherits(&NumberObject::info) && !asObject(name)->inherits(&StringObject::info)) + continue; + propertyName = name.toString(exec); + if (exec->hadException()) + break; + m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); + } } return; } @@ -354,7 +378,10 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& return StringifySucceeded; } - value = unwrapBoxedPrimitive(value); + value = unwrapBoxedPrimitive(m_exec, value); + + if (m_exec->hadException()) + return StringifyFailed; if (value.isBoolean()) { builder.append(value.getBoolean() ? "true" : "false"); @@ -381,6 +408,15 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& JSObject* object = asObject(value); + CallData callData; + if (object->getCallData(callData) != CallTypeNone) { + if (holder->inherits(&JSArray::info)) { + builder.append("null"); + return StringifySucceeded; + } + return StringifyFailedDueToUndefinedValue; + } + // Handle cycle detection, and put the holder on the stack. if (!m_holderCycleDetector.add(object).second) { throwError(m_exec, TypeError, "JSON.stringify cannot serialize cyclic structures."); @@ -572,13 +608,12 @@ const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable }; bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { - const HashEntry* entry = ExecState::jsonTable(exec)->entry(exec, propertyName); - if (!entry) - return JSObject::getOwnPropertySlot(exec, propertyName, slot); + return getStaticFunctionSlot<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, slot); +} - ASSERT(entry->attributes() & Function); - setUpStaticFunctionSlot(exec, entry, this, propertyName, slot); - return true; +bool JSONObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, descriptor); } void JSONObject::markStringifiers(MarkStack& markStack, Stringifier* stringifier) @@ -597,11 +632,11 @@ public: } JSValue walk(JSValue unfiltered); private: - JSValue callReviver(JSValue property, JSValue unfiltered) + JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered) { JSValue args[] = { property, unfiltered }; ArgList argList(args, 2); - return call(m_exec, m_function, m_callType, m_callData, jsNull(), argList); + return call(m_exec, m_function, m_callType, m_callData, thisObj, argList); } friend class Holder; @@ -611,7 +646,10 @@ private: CallType m_callType; CallData m_callData; }; - + +// We clamp recursion well beyond anything reasonable, but we also have a timeout check +// to guard against "infinite" execution by inserting arbitrarily large objects. +static const unsigned maximumFilterRecursion = 40000; enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember }; NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) @@ -625,12 +663,21 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) WalkerState state = StateUnknown; JSValue inValue = unfiltered; JSValue outValue = jsNull(); + + TimeoutChecker localTimeoutChecker(m_exec->globalData().timeoutChecker); + localTimeoutChecker.reset(); + unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck(); while (1) { switch (state) { arrayStartState: case ArrayStartState: { ASSERT(inValue.isObject()); - ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue))); + ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue)) || asObject(inValue)->inherits(&JSArray::info)); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) { + m_exec->setException(createStackOverflowError(m_exec)); + return jsUndefined(); + } + JSArray* array = asArray(inValue); arrayStack.append(array); indexStack.append(0); @@ -638,6 +685,14 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) } arrayStartVisitMember: case ArrayStartVisitMember: { + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) { + m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); + return jsUndefined(); + } + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + JSArray* array = arrayStack.last(); uint32_t index = indexStack.last(); if (index == array->length()) { @@ -646,7 +701,16 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) indexStack.removeLast(); break; } - inValue = array->getIndex(index); + if (isJSArray(&m_exec->globalData(), array) && array->canGetIndex(index)) + inValue = array->getIndex(index); + else { + PropertySlot slot; + if (array->getOwnPropertySlot(m_exec, index, slot)) + inValue = slot.getValue(m_exec, index); + else + inValue = jsUndefined(); + } + if (inValue.isObject()) { stateStack.append(ArrayEndVisitMember); goto stateUnknown; @@ -656,7 +720,15 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) } case ArrayEndVisitMember: { JSArray* array = arrayStack.last(); - array->setIndex(indexStack.last(), callReviver(jsString(m_exec, UString::from(indexStack.last())), outValue)); + JSValue filteredValue = callReviver(array, jsString(m_exec, UString::from(indexStack.last())), outValue); + if (filteredValue.isUndefined()) + array->deleteProperty(m_exec, indexStack.last()); + else { + if (isJSArray(&m_exec->globalData(), array) && array->canSetIndex(indexStack.last())) + array->setIndex(indexStack.last(), filteredValue); + else + array->put(m_exec, indexStack.last(), filteredValue); + } if (m_exec->hadException()) return jsNull(); indexStack.last()++; @@ -665,7 +737,12 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) objectStartState: case ObjectStartState: { ASSERT(inValue.isObject()); - ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue))); + ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::info)); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) { + m_exec->setException(createStackOverflowError(m_exec)); + return jsUndefined(); + } + JSObject* object = asObject(inValue); objectStack.append(object); indexStack.append(0); @@ -675,6 +752,14 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) } objectStartVisitMember: case ObjectStartVisitMember: { + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) { + m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); + return jsUndefined(); + } + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + JSObject* object = objectStack.last(); uint32_t index = indexStack.last(); PropertyNameArray& properties = propertyStack.last(); @@ -686,9 +771,15 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) break; } PropertySlot slot; - object->getOwnPropertySlot(m_exec, properties[index], slot); - inValue = slot.getValue(m_exec, properties[index]); - ASSERT(!m_exec->hadException()); + if (object->getOwnPropertySlot(m_exec, properties[index], slot)) + inValue = slot.getValue(m_exec, properties[index]); + else + inValue = jsUndefined(); + + // The holder may be modified by the reviver function so any lookup may throw + if (m_exec->hadException()) + return jsNull(); + if (inValue.isObject()) { stateStack.append(ObjectEndVisitMember); goto stateUnknown; @@ -700,7 +791,11 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) JSObject* object = objectStack.last(); Identifier prop = propertyStack.last()[indexStack.last()]; PutPropertySlot slot; - object->put(m_exec, prop, callReviver(jsString(m_exec, prop.ustring()), outValue), slot); + JSValue filteredValue = callReviver(object, jsString(m_exec, prop.ustring()), outValue); + if (filteredValue.isUndefined()) + object->deleteProperty(m_exec, prop); + else + object->put(m_exec, prop, filteredValue, slot); if (m_exec->hadException()) return jsNull(); indexStack.last()++; @@ -712,16 +807,29 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) outValue = inValue; break; } - if (isJSArray(&m_exec->globalData(), asObject(inValue))) + JSObject* object = asObject(inValue); + if (isJSArray(&m_exec->globalData(), object) || object->inherits(&JSArray::info)) goto arrayStartState; goto objectStartState; } if (stateStack.isEmpty()) break; + state = stateStack.last(); stateStack.removeLast(); + + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) { + m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); + return jsUndefined(); + } + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } } - return callReviver(jsEmptyString(m_exec), outValue); + JSObject* finalHolder = constructEmptyObject(m_exec); + PutPropertySlot slot; + finalHolder->put(m_exec, m_exec->globalData().propertyNames->emptyIdentifier, outValue, slot); + return callReviver(finalHolder, jsEmptyString(m_exec), outValue); } // ECMA-262 v5 15.12.2 diff --git a/JavaScriptCore/runtime/JSONObject.h b/JavaScriptCore/runtime/JSONObject.h index faca7c7..65c9803 100644 --- a/JavaScriptCore/runtime/JSONObject.h +++ b/JavaScriptCore/runtime/JSONObject.h @@ -34,20 +34,21 @@ namespace JSC { class JSONObject : public JSObject { public: - JSONObject(PassRefPtr<Structure> structure) + JSONObject(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { } static PassRefPtr<Structure> createStructure(JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType)); + return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark | HasDefaultGetPropertyNames)); } static void markStringifiers(MarkStack&, Stringifier*); private: virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp index 419dfe9..db2a9b2 100644 --- a/JavaScriptCore/runtime/JSObject.cpp +++ b/JavaScriptCore/runtime/JSObject.cpp @@ -30,6 +30,7 @@ #include "JSGlobalObject.h" #include "NativeErrorConstructor.h" #include "ObjectPrototype.h" +#include "PropertyDescriptor.h" #include "PropertyNameArray.h" #include "Lookup.h" #include "Nodes.h" @@ -37,43 +38,22 @@ #include <math.h> #include <wtf/Assertions.h> -#define JSOBJECT_MARK_TRACING 0 - -#if JSOBJECT_MARK_TRACING - -#define JSOBJECT_MARK_BEGIN() \ - static int markStackDepth = 0; \ - for (int i = 0; i < markStackDepth; i++) \ - putchar('-'); \ - printf("%s (%p)\n", className().UTF8String().c_str(), this); \ - markStackDepth++; \ - -#define JSOBJECT_MARK_END() \ - markStackDepth--; - -#else // JSOBJECT_MARK_TRACING - -#define JSOBJECT_MARK_BEGIN() -#define JSOBJECT_MARK_END() - -#endif // JSOBJECT_MARK_TRACING - namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSObject); void JSObject::markChildren(MarkStack& markStack) { - JSOBJECT_MARK_BEGIN(); +#ifndef NDEBUG + bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation; + markStack.m_isCheckingForDefaultMarkViolation = false; +#endif - JSCell::markChildren(markStack); - m_structure->markAggregate(markStack); + markChildrenDirect(markStack); - PropertyStorage storage = propertyStorage(); - size_t storageSize = m_structure->propertyStorageSize(); - markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize); - - JSOBJECT_MARK_END(); +#ifndef NDEBUG + markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; +#endif } UString JSObject::className() const @@ -295,7 +275,7 @@ const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifi return 0; } -void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction) +void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) { JSValue object = getDirect(propertyName); if (object && object.isGetterSetter()) { @@ -306,7 +286,7 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO PutPropertySlot slot; GetterSetter* getterSetter = new (exec) GetterSetter(exec); - putDirectInternal(exec->globalData(), propertyName, getterSetter, Getter, true, slot); + putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot); // putDirect will change our Structure if we add a new property. For // getters and setters, though, we also need to change our Structure @@ -322,7 +302,7 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO getterSetter->setGetter(getterFunction); } -void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction) +void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) { JSValue object = getDirect(propertyName); if (object && object.isGetterSetter()) { @@ -333,7 +313,7 @@ void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSO PutPropertySlot slot; GetterSetter* getterSetter = new (exec) GetterSetter(exec); - putDirectInternal(exec->globalData(), propertyName, getterSetter, Setter, true, slot); + putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot); // putDirect will change our Structure if we add a new property. For // getters and setters, though, we also need to change our Structure @@ -447,6 +427,11 @@ void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyName m_structure->getEnumerablePropertyNames(exec, propertyNames, this); } +void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + m_structure->getOwnEnumerablePropertyNames(exec, propertyNames, this); +} + bool JSObject::toBoolean(ExecState*) const { return true; @@ -486,7 +471,7 @@ JSObject* JSObject::unwrappedObject() void JSObject::removeDirect(const Identifier& propertyName) { size_t offset; - if (m_structure->isDictionary()) { + if (m_structure->isUncacheableDictionary()) { offset = m_structure->removePropertyWithoutTransition(propertyName); if (offset != WTF::notFound) putDirectOffset(offset, jsUndefined()); @@ -528,9 +513,154 @@ void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize) allocatePropertyStorageInline(oldSize, newSize); } -JSObject* constructEmptyObject(ExecState* exec) +bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + unsigned attributes = 0; + JSCell* cell = 0; + size_t offset = m_structure->get(propertyName, attributes, cell); + if (offset == WTF::notFound) + return false; + descriptor.setDescriptor(getDirectOffset(offset), attributes); + return true; +} + +bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { - return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); + JSObject* object = this; + while (true) { + if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) + return true; + JSValue prototype = object->prototype(); + if (!prototype.isObject()) + return false; + object = asObject(prototype); + } +} + +static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue) +{ + if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { + target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter)); + return true; + } + attributes &= ~ReadOnly; + if (descriptor.getter() && descriptor.getter().isObject()) + target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes); + if (exec->hadException()) + return false; + if (descriptor.setter() && descriptor.setter().isObject()) + target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes); + return !exec->hadException(); +} + +bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) +{ + // If we have a new property we can just put it on normally + PropertyDescriptor current; + if (!getOwnPropertyDescriptor(exec, propertyName, current)) + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined()); + + if (descriptor.isEmpty()) + return true; + + if (current.equalTo(descriptor)) + return true; + + // Filter out invalid changes + if (!current.configurable()) { + if (descriptor.configurable()) { + if (throwException) + throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property."); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { + if (throwException) + throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property."); + return false; + } + } + + // A generic descriptor is simply changing the attributes of an existing property + if (descriptor.isGenericDescriptor()) { + if (!current.attributesEqual(descriptor)) { + deleteProperty(exec, propertyName); + putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value()); + } + return true; + } + + // Changing between a normal property or an accessor property + if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { + if (!current.configurable()) { + if (throwException) + throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property."); + return false; + } + deleteProperty(exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined()); + } + + // Changing the value and attributes of an existing property + if (descriptor.isDataDescriptor()) { + if (!current.configurable()) { + if (!current.writable() && descriptor.writable()) { + if (throwException) + throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property."); + return false; + } + if (!current.writable()) { + if (descriptor.value() || !JSValue::strictEqual(current.value(), descriptor.value())) { + if (throwException) + throwError(exec, TypeError, "Attempting to change value of a readonly property."); + return false; + } + } + } else if (current.attributesEqual(descriptor)) { + if (!descriptor.value()) + return true; + PutPropertySlot slot; + put(exec, propertyName, descriptor.value(), slot); + if (exec->hadException()) + return false; + return true; + } + deleteProperty(exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value()); + } + + // Changing the accessor functions of an existing accessor property + ASSERT(descriptor.isAccessorDescriptor()); + if (!current.configurable()) { + if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(current.setter(), descriptor.setter()))) { + if (throwException) + throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property."); + return false; + } + if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(current.getter(), descriptor.getter()))) { + if (throwException) + throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property."); + return false; + } + } + JSValue accessor = getDirect(propertyName); + if (!accessor) + return false; + GetterSetter* getterSetter = asGetterSetter(accessor); + if (current.attributesEqual(descriptor)) { + if (descriptor.setter()) + getterSetter->setSetter(asObject(descriptor.setter())); + if (descriptor.getter()) + getterSetter->setGetter(asObject(descriptor.getter())); + return true; + } + deleteProperty(exec, propertyName); + unsigned attrs = current.attributesWithOverride(descriptor); + if (descriptor.setter()) + attrs |= Setter; + if (descriptor.getter()) + attrs |= Getter; + putDirect(propertyName, getterSetter, attrs); + return true; } } // namespace JSC diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h index decd5e9..84b5f4b 100644 --- a/JavaScriptCore/runtime/JSObject.h +++ b/JavaScriptCore/runtime/JSObject.h @@ -27,7 +27,9 @@ #include "ClassInfo.h" #include "CommonIdentifiers.h" #include "CallFrame.h" +#include "JSCell.h" #include "JSNumberCell.h" +#include "MarkStack.h" #include "PropertySlot.h" #include "PutPropertySlot.h" #include "ScopeChain.h" @@ -46,6 +48,7 @@ namespace JSC { class HashEntry; class InternalFunction; + class PropertyDescriptor; class PropertyNameArray; class Structure; struct HashTable; @@ -71,20 +74,19 @@ namespace JSC { friend class JSCell; public: - explicit JSObject(PassRefPtr<Structure>); + explicit JSObject(NonNullPassRefPtr<Structure>); virtual void markChildren(MarkStack&); + ALWAYS_INLINE void markChildrenDirect(MarkStack& markStack); // The inline virtual destructor cannot be the first virtual function declared // in the class as it results in the vtable being generated as a weak symbol virtual ~JSObject(); - bool inherits(const ClassInfo* classInfo) const { return JSCell::isObject(classInfo); } - JSValue prototype() const; void setPrototype(JSValue prototype); - void setStructure(PassRefPtr<Structure>); + void setStructure(NonNullPassRefPtr<Structure>); Structure* inheritorID(); virtual UString className() const; @@ -94,9 +96,11 @@ namespace JSC { bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&); virtual void put(ExecState*, unsigned propertyName, JSValue value); @@ -119,6 +123,7 @@ namespace JSC { virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty); virtual void getPropertyNames(ExecState*, PropertyNameArray&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&); virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); @@ -181,10 +186,11 @@ namespace JSC { void fillGetterPropertySlot(PropertySlot&, JSValue* location); - virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction); - virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction); + virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0); + virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName); virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName); + virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); virtual bool isGlobalObject() const { return false; } virtual bool isVariableObject() const { return false; } @@ -201,10 +207,33 @@ namespace JSC { static PassRefPtr<Structure> createStructure(JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot)); + return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark | HasDefaultGetPropertyNames)); + } + + protected: + void addAnonymousSlots(unsigned count); + void putAnonymousValue(unsigned index, JSValue value) + { + *locationForOffset(index) = value; + } + JSValue getAnonymousValue(unsigned index) + { + return *locationForOffset(index); } private: + // Nobody should ever ask any of these questions on something already known to be a JSObject. + using JSCell::isAPIValueWrapper; + using JSCell::isGetterSetter; + using JSCell::toObject; + void getObject(); + void getString(); + void isObject(); + void isString(); +#if USE(JSVALUE32) + void isNumber(); +#endif + ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } @@ -235,18 +264,20 @@ namespace JSC { RefPtr<Structure> m_inheritorID; }; -JSObject* constructEmptyObject(ExecState*); +inline JSObject* asObject(JSCell* cell) +{ + ASSERT(cell->isObject()); + return static_cast<JSObject*>(cell); +} inline JSObject* asObject(JSValue value) { - ASSERT(asCell(value)->isObject()); - return static_cast<JSObject*>(asCell(value)); + return asObject(value.asCell()); } -inline JSObject::JSObject(PassRefPtr<Structure> structure) +inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure) : JSCell(structure.releaseRef()) // ~JSObject balances this ref() { - ASSERT(m_structure); ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); ASSERT(m_structure->isEmpty()); ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); @@ -275,7 +306,7 @@ inline void JSObject::setPrototype(JSValue prototype) setStructure(newStructure.release()); } -inline void JSObject::setStructure(PassRefPtr<Structure> structure) +inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure) { m_structure->deref(); m_structure = structure.releaseRef(); // ~JSObject balances this ref() @@ -293,7 +324,7 @@ inline bool Structure::isUsingInlineStorage() const return (propertyStorageCapacity() == JSObject::inlineStorageCapacity); } -inline bool JSCell::isObject(const ClassInfo* info) const +inline bool JSCell::inherits(const ClassInfo* info) const { for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { if (ci == info) @@ -302,10 +333,10 @@ inline bool JSCell::isObject(const ClassInfo* info) const return false; } -// this method is here to be after the inline declaration of JSCell::isObject -inline bool JSValue::isObject(const ClassInfo* classInfo) const +// this method is here to be after the inline declaration of JSCell::inherits +inline bool JSValue::inherits(const ClassInfo* classInfo) const { - return isCell() && asCell()->isObject(classInfo); + return isCell() && asCell()->inherits(classInfo); } ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -454,7 +485,18 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue return; } + // If we have a specific function, we may have got to this point if there is + // already a transition with the correct property name and attributes, but + // specialized to a different function. In this case we just want to give up + // and despecialize the transition. + // In this case we clear the value of specificFunction which will result + // in us adding a non-specific transition, and any subsequent lookup in + // Structure::addPropertyTransitionToExistingStructure will just use that. + if (specificFunction && m_structure->hasTransition(propertyName, attributes)) + specificFunction = 0; + RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset); + if (currentCapacity != structure->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); @@ -480,6 +522,17 @@ inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifi putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); } +inline void JSObject::addAnonymousSlots(unsigned count) +{ + size_t currentCapacity = m_structure->propertyStorageCapacity(); + RefPtr<Structure> structure = Structure::addAnonymousSlotsTransition(m_structure, count); + + if (currentCapacity != structure->propertyStorageCapacity()) + allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); + + setStructure(structure.release()); +} + inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { ASSERT(value); @@ -555,8 +608,7 @@ inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, Pro while (true) { if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); - ASSERT(cell->isObject()); - JSValue prototype = static_cast<JSObject*>(cell)->prototype(); + JSValue prototype = asObject(cell)->prototype(); if (!prototype.isObject()) return jsUndefined(); cell = asObject(prototype); @@ -581,8 +633,7 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot while (true) { if (cell->getOwnPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); - ASSERT(cell->isObject()); - JSValue prototype = static_cast<JSObject*>(cell)->prototype(); + JSValue prototype = asObject(cell)->prototype(); if (!prototype.isObject()) return jsUndefined(); cell = prototype.asCell(); @@ -627,6 +678,17 @@ ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_ m_externalStorage = newPropertyStorage; } +ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) +{ + JSCell::markChildren(markStack); + + m_structure->markAggregate(markStack); + + PropertyStorage storage = propertyStorage(); + size_t storageSize = m_structure->propertyStorageSize(); + markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize); +} + } // namespace JSC #endif // JSObject_h diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp index dc0304f..e08a3d9 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -37,42 +37,6 @@ JSPropertyNameIterator::~JSPropertyNameIterator() { } -JSValue JSPropertyNameIterator::toPrimitive(ExecState*, PreferredPrimitiveType) const -{ - ASSERT_NOT_REACHED(); - return JSValue(); -} - -bool JSPropertyNameIterator::getPrimitiveNumber(ExecState*, double&, JSValue&) -{ - ASSERT_NOT_REACHED(); - return false; -} - -bool JSPropertyNameIterator::toBoolean(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return false; -} - -double JSPropertyNameIterator::toNumber(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return 0; -} - -UString JSPropertyNameIterator::toString(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return ""; -} - -JSObject* JSPropertyNameIterator::toObject(ExecState*) const -{ - ASSERT_NOT_REACHED(); - return 0; -} - void JSPropertyNameIterator::markChildren(MarkStack& markStack) { JSCell::markChildren(markStack); diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.h b/JavaScriptCore/runtime/JSPropertyNameIterator.h index 4534528..d2849a8 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.h +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -44,13 +44,6 @@ namespace JSC { virtual ~JSPropertyNameIterator(); - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; - virtual bool getPrimitiveNumber(ExecState*, double&, JSValue&); - virtual bool toBoolean(ExecState*) const; - virtual double toNumber(ExecState*) const; - virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; - virtual void markChildren(MarkStack&); JSValue next(ExecState*); diff --git a/JavaScriptCore/runtime/JSString.cpp b/JavaScriptCore/runtime/JSString.cpp index 86f95e0..91ddaeb 100644 --- a/JavaScriptCore/runtime/JSString.cpp +++ b/JavaScriptCore/runtime/JSString.cpp @@ -103,6 +103,33 @@ bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyNam return true; } +bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(exec, m_value.size()), DontEnum | DontDelete | ReadOnly); + return true; + } + + bool isStrictUInt32; + unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); + if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) { + descriptor.setDescriptor(jsSingleCharacterSubstring(exec, m_value, i), DontDelete | ReadOnly); + return true; + } + + return false; +} + +bool JSString::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (getStringPropertyDescriptor(exec, propertyName, descriptor)) + return true; + if (propertyName != exec->propertyNames().underscoreProto) + return false; + descriptor.setDescriptor(exec->lexicalGlobalObject()->stringPrototype(), DontEnum); + return true; +} + bool JSString::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { // The semantics here are really getPropertySlot, not getOwnPropertySlot. diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index 3daf58a..1e46551 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -27,6 +27,7 @@ #include "CommonIdentifiers.h" #include "Identifier.h" #include "JSNumberCell.h" +#include "PropertyDescriptor.h" #include "PropertySlot.h" namespace JSC { @@ -86,11 +87,12 @@ namespace JSC { bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); bool canGetIndex(unsigned i) { return i < static_cast<unsigned>(m_value.size()); } JSString* getIndex(JSGlobalData*, unsigned); - static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, NeedsThisConversion)); } + static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, NeedsThisConversion | HasDefaultMark)); } private: enum VPtrStealingHackType { VPtrStealingHack }; @@ -113,6 +115,7 @@ namespace JSC { // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); UString m_value; }; @@ -211,6 +214,26 @@ namespace JSC { return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec)); } + inline UString JSValue::toString(ExecState* exec) const + { + if (isString()) + return static_cast<JSString*>(asCell())->value(); + if (isInt32()) + return exec->globalData().numericStrings.add(asInt32()); + if (isDouble()) + return exec->globalData().numericStrings.add(asDouble()); + if (isTrue()) + return "true"; + if (isFalse()) + return "false"; + if (isNull()) + return "null"; + if (isUndefined()) + return "undefined"; + ASSERT(isCell()); + return asCell()->toString(exec); + } + } // namespace JSC #endif // JSString_h diff --git a/JavaScriptCore/runtime/JSType.h b/JavaScriptCore/runtime/JSType.h index a118b87..882b218 100644 --- a/JavaScriptCore/runtime/JSType.h +++ b/JavaScriptCore/runtime/JSType.h @@ -33,7 +33,6 @@ namespace JSC { NumberType = 3, NullType = 4, StringType = 5, - // The CompoundType value must come before any JSType that may have children CompoundType = 6, ObjectType = 7, diff --git a/JavaScriptCore/runtime/TypeInfo.h b/JavaScriptCore/runtime/JSTypeInfo.h index 70aeed3..279510b 100644 --- a/JavaScriptCore/runtime/TypeInfo.h +++ b/JavaScriptCore/runtime/JSTypeInfo.h @@ -24,8 +24,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TypeInfo_h -#define TypeInfo_h +#ifndef JSTypeInfo_h +#define JSTypeInfo_h + +// This file would be called TypeInfo.h, but that conflicts with <typeinfo.h> +// in the STL on systems without case-sensitive file systems. #include "JSType.h" @@ -38,6 +41,8 @@ namespace JSC { static const unsigned ImplementsDefaultHasInstance = 1 << 3; static const unsigned NeedsThisConversion = 1 << 4; static const unsigned HasStandardGetOwnPropertySlot = 1 << 5; + static const unsigned HasDefaultMark = 1 << 6; + static const unsigned HasDefaultGetPropertyNames = 1 << 7; class TypeInfo { friend class JIT; @@ -59,7 +64,8 @@ namespace JSC { bool overridesHasInstance() const { return m_flags & OverridesHasInstance; } bool needsThisConversion() const { return m_flags & NeedsThisConversion; } bool hasStandardGetOwnPropertySlot() const { return m_flags & HasStandardGetOwnPropertySlot; } - + bool hasDefaultMark() const { return m_flags & HasDefaultMark; } + bool hasDefaultGetPropertyNames() const { return m_flags & HasDefaultGetPropertyNames; } unsigned flags() const { return m_flags; } private: @@ -69,4 +75,4 @@ namespace JSC { } -#endif // TypeInfo_h +#endif // JSTypeInfo_h diff --git a/JavaScriptCore/runtime/JSValue.cpp b/JavaScriptCore/runtime/JSValue.cpp index 39a4093..699c1cd 100644 --- a/JavaScriptCore/runtime/JSValue.cpp +++ b/JavaScriptCore/runtime/JSValue.cpp @@ -110,7 +110,10 @@ char* JSValue::description() { static const size_t size = 32; static char description[size]; - if (isInt32()) + + if (!*this) + snprintf(description, size, "<JSValue()>"); + else if (isInt32()) snprintf(description, size, "Int32: %d", asInt32()); else if (isDouble()) snprintf(description, size, "Double: %lf", asDouble()); diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h index 408c187..3c511d8 100644 --- a/JavaScriptCore/runtime/JSValue.h +++ b/JavaScriptCore/runtime/JSValue.h @@ -20,15 +20,14 @@ * */ -#include <stddef.h> // for size_t -#include <stdint.h> - #ifndef JSValue_h #define JSValue_h #include "CallData.h" #include "ConstructData.h" #include <math.h> +#include <stddef.h> // for size_t +#include <stdint.h> #include <wtf/AlwaysInline.h> #include <wtf/Assertions.h> #include <wtf/HashTraits.h> @@ -42,7 +41,6 @@ namespace JSC { class JSImmediate; class JSObject; class JSString; - class MarkStack; class PropertySlot; class PutPropertySlot; class UString; @@ -130,7 +128,7 @@ namespace JSC { bool isString() const; bool isGetterSetter() const; bool isObject() const; - bool isObject(const ClassInfo*) const; + bool inherits(const ClassInfo*) const; // Extracting the value. bool getBoolean(bool&) const; @@ -172,12 +170,6 @@ namespace JSC { // signle precision float is not a representation used in JS or JSC). float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); } - // Garbage collection. - void markChildren(MarkStack&); - bool hasChildren() const; - bool marked() const; - void markDirect(); - // Object operations, with the toObject operation included. JSValue get(ExecState*, const Identifier& propertyName) const; JSValue get(ExecState*, const Identifier& propertyName, PropertySlot&) const; @@ -221,7 +213,8 @@ namespace JSC { enum { FalseTag = 0xfffffffc }; enum { NullTag = 0xfffffffb }; enum { UndefinedTag = 0xfffffffa }; - enum { DeletedValueTag = 0xfffffff9 }; + enum { EmptyValueTag = 0xfffffff9 }; + enum { DeletedValueTag = 0xfffffff8 }; enum { LowestTag = DeletedValueTag }; @@ -435,7 +428,7 @@ namespace JSC { inline JSValue::JSValue() { - u.asBits.tag = CellTag; + u.asBits.tag = EmptyValueTag; u.asBits.payload = 0; } @@ -471,19 +464,26 @@ namespace JSC { inline JSValue::JSValue(JSCell* ptr) { - u.asBits.tag = CellTag; + if (ptr) + u.asBits.tag = CellTag; + else + u.asBits.tag = EmptyValueTag; u.asBits.payload = reinterpret_cast<int32_t>(ptr); } inline JSValue::JSValue(const JSCell* ptr) { - u.asBits.tag = CellTag; + if (ptr) + u.asBits.tag = CellTag; + else + u.asBits.tag = EmptyValueTag; u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); } inline JSValue::operator bool() const { - return u.asBits.payload || tag() != CellTag; + ASSERT(tag() != DeletedValueTag); + return tag() != EmptyValueTag; } inline bool JSValue::operator==(const JSValue& other) const diff --git a/JavaScriptCore/runtime/JSVariableObject.cpp b/JavaScriptCore/runtime/JSVariableObject.cpp index a36cefa..6586393 100644 --- a/JavaScriptCore/runtime/JSVariableObject.cpp +++ b/JavaScriptCore/runtime/JSVariableObject.cpp @@ -30,6 +30,7 @@ #include "JSVariableObject.h" #include "PropertyNameArray.h" +#include "PropertyDescriptor.h" namespace JSC { @@ -41,7 +42,7 @@ bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propert return JSObject::deleteProperty(exec, propertyName); } -void JSVariableObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +void JSVariableObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) { SymbolTable::const_iterator end = symbolTable().end(); for (SymbolTable::const_iterator it = symbolTable().begin(); it != end; ++it) { @@ -49,7 +50,7 @@ void JSVariableObject::getPropertyNames(ExecState* exec, PropertyNameArray& prop propertyNames.add(Identifier(exec, it->first.get())); } - JSObject::getPropertyNames(exec, propertyNames); + JSObject::getOwnPropertyNames(exec, propertyNames); } bool JSVariableObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const @@ -67,4 +68,14 @@ bool JSVariableObject::isVariableObject() const return true; } +bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.ustring().rep()); + if (!entry.isNull()) { + descriptor.setDescriptor(registerAt(entry.getIndex()).jsValue(), entry.getAttributes() | DontDelete); + return true; + } + return false; +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSVariableObject.h b/JavaScriptCore/runtime/JSVariableObject.h index b969da5..66e78c3 100644 --- a/JavaScriptCore/runtime/JSVariableObject.h +++ b/JavaScriptCore/runtime/JSVariableObject.h @@ -49,7 +49,7 @@ namespace JSC { virtual void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes) = 0; virtual bool deleteProperty(ExecState*, const Identifier&); - virtual void getPropertyNames(ExecState*, PropertyNameArray&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&); virtual bool isVariableObject() const; virtual bool isDynamicScope() const = 0; @@ -58,6 +58,11 @@ namespace JSC { Register& registerAt(int index) const { return d->registers[index]; } + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark)); + } + protected: // Subclasses of JSVariableObject can subclass this struct to add data // without increasing their own size (since there's a hard limit on the @@ -79,7 +84,7 @@ namespace JSC { JSVariableObjectData& operator=(const JSVariableObjectData&); }; - JSVariableObject(PassRefPtr<Structure> structure, JSVariableObjectData* data) + JSVariableObject(NonNullPassRefPtr<Structure> structure, JSVariableObjectData* data) : JSObject(structure) , d(data) // Subclass owns this pointer. { @@ -89,6 +94,7 @@ namespace JSC { void setRegisters(Register* r, Register* registerArray); bool symbolTableGet(const Identifier&, PropertySlot&); + bool symbolTableGet(const Identifier&, PropertyDescriptor&); bool symbolTableGet(const Identifier&, PropertySlot&, bool& slotIsWriteable); bool symbolTablePut(const Identifier&, JSValue); bool symbolTablePutWithAttributes(const Identifier&, JSValue, unsigned attributes); diff --git a/JavaScriptCore/runtime/JSWrapperObject.h b/JavaScriptCore/runtime/JSWrapperObject.h index 0b2c680..723b75d 100644 --- a/JavaScriptCore/runtime/JSWrapperObject.h +++ b/JavaScriptCore/runtime/JSWrapperObject.h @@ -25,33 +25,41 @@ #include "JSObject.h" namespace JSC { - + // This class is used as a base for classes such as String, // Number, Boolean and Date which are wrappers for primitive types. class JSWrapperObject : public JSObject { protected: - explicit JSWrapperObject(PassRefPtr<Structure>); + explicit JSWrapperObject(NonNullPassRefPtr<Structure>); public: JSValue internalValue() const { return m_internalValue; } void setInternalValue(JSValue); - + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultGetPropertyNames | HasDefaultMark)); + } + + private: virtual void markChildren(MarkStack&); - private: JSValue m_internalValue; }; - - inline JSWrapperObject::JSWrapperObject(PassRefPtr<Structure> structure) + + inline JSWrapperObject::JSWrapperObject(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { + addAnonymousSlots(1); + putAnonymousValue(0, jsNull()); } - + inline void JSWrapperObject::setInternalValue(JSValue value) { ASSERT(value); ASSERT(!value.isObject()); m_internalValue = value; + putAnonymousValue(0, value); } } // namespace JSC diff --git a/JavaScriptCore/runtime/LiteralParser.cpp b/JavaScriptCore/runtime/LiteralParser.cpp index 17ec906..d242282 100644 --- a/JavaScriptCore/runtime/LiteralParser.cpp +++ b/JavaScriptCore/runtime/LiteralParser.cpp @@ -295,7 +295,10 @@ JSValue LiteralParser::parse(ParserState initialState) } doParseArrayStartExpression: case DoParseArrayStartExpression: { + TokenType lastToken = m_lexer.currentToken().type; if (m_lexer.next() == TokRBracket) { + if (lastToken == TokComma) + return JSValue(); m_lexer.next(); lastValue = objectStack.last(); objectStack.removeLast(); diff --git a/JavaScriptCore/runtime/LiteralParser.h b/JavaScriptCore/runtime/LiteralParser.h index bceee7c..0f8072b 100644 --- a/JavaScriptCore/runtime/LiteralParser.h +++ b/JavaScriptCore/runtime/LiteralParser.h @@ -89,7 +89,7 @@ namespace JSC { private: TokenType lex(LiteralParserToken&); - template <ParserMode parserMode> TokenType lexString(LiteralParserToken&); + template <ParserMode mode> TokenType lexString(LiteralParserToken&); TokenType lexNumber(LiteralParserToken&); LiteralParserToken m_currentToken; UString m_string; diff --git a/JavaScriptCore/runtime/Lookup.h b/JavaScriptCore/runtime/Lookup.h index 167f2bc..4d70689 100644 --- a/JavaScriptCore/runtime/Lookup.h +++ b/JavaScriptCore/runtime/Lookup.h @@ -51,7 +51,7 @@ namespace JSC { typedef PropertySlot::GetValueFunc GetFunction; typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value); - class HashEntry { + class HashEntry : public FastAllocBase { public: void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2) { @@ -186,6 +186,24 @@ namespace JSC { return true; } + template <class ThisImp, class ParentImp> + inline bool getStaticPropertyDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor); + + PropertySlot slot; + if (entry->attributes() & Function) + setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + else + slot.setCustom(thisObj, entry->propertyGetter()); + + descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); + return true; + } + /** * Simplified version of getStaticPropertySlot in case there are only functions. * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing @@ -204,6 +222,27 @@ namespace JSC { setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); return true; } + + /** + * Simplified version of getStaticPropertyDescriptor in case there are only functions. + * Using this instead of getStaticPropertyDescriptor allows 'this' to avoid implementing + * a dummy getValueProperty. + */ + template <class ParentImp> + inline bool getStaticFunctionDescriptor(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor)) + return true; + + const HashEntry* entry = table->entry(exec, propertyName); + if (!entry) + return false; + + PropertySlot slot; + setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); + return true; + } /** * Simplified version of getStaticPropertySlot in case there are no functions, only "values". @@ -224,6 +263,25 @@ namespace JSC { } /** + * Simplified version of getStaticPropertyDescriptor in case there are no functions, only "values". + * Using this instead of getStaticPropertyDescriptor removes the need for a FuncImp class. + */ + template <class ThisImp, class ParentImp> + inline bool getStaticValueDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor); + + ASSERT(!(entry->attributes() & Function)); + PropertySlot slot; + slot.setCustom(thisObj, entry->propertyGetter()); + descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); + return true; + } + + /** * This one is for "put". * It looks up a hash entry for the property to be set. If an entry * is found it sets the value and returns true, else it returns false. diff --git a/JavaScriptCore/runtime/MarkStack.cpp b/JavaScriptCore/runtime/MarkStack.cpp index 80dbb17..a350c35 100644 --- a/JavaScriptCore/runtime/MarkStack.cpp +++ b/JavaScriptCore/runtime/MarkStack.cpp @@ -26,8 +26,7 @@ #include "config.h" #include "MarkStack.h" -namespace JSC -{ +namespace JSC { size_t MarkStack::s_pageSize = 0; diff --git a/JavaScriptCore/runtime/MarkStack.h b/JavaScriptCore/runtime/MarkStack.h index 7a7b3af..ba00057 100644 --- a/JavaScriptCore/runtime/MarkStack.h +++ b/JavaScriptCore/runtime/MarkStack.h @@ -27,33 +27,27 @@ #define MarkStack_h #include "JSValue.h" - #include <wtf/Noncopyable.h> namespace JSC { + + class JSGlobalData; class Register; enum MarkSetProperties { MayContainNullValues, NoNullValues }; class MarkStack : Noncopyable { public: - MarkStack() - : m_markSets() - , m_values() - { - } - - ALWAYS_INLINE void append(JSValue value) + MarkStack(void* jsArrayVPtr) + : m_jsArrayVPtr(jsArrayVPtr) +#ifndef NDEBUG + , m_isCheckingForDefaultMarkViolation(false) +#endif { - ASSERT(value); - if (value.marked()) - return; - value.markDirect(); - if (value.hasChildren()) - m_values.append(value.asCell()); } - ALWAYS_INLINE void append(JSCell* cell); + ALWAYS_INLINE void append(JSValue); + ALWAYS_INLINE void append(JSCell*); ALWAYS_INLINE void appendValues(Register* values, size_t count, MarkSetProperties properties = NoNullValues) { @@ -76,12 +70,15 @@ namespace JSC { } private: + void markChildren(JSCell*); + struct MarkSet { MarkSet(JSValue* values, JSValue* end, MarkSetProperties properties) : m_values(values) , m_end(end) , m_properties(properties) { + ASSERT(values); } JSValue* m_values; JSValue* m_end; @@ -136,6 +133,12 @@ namespace JSC { ASSERT(m_top); return m_data[--m_top]; } + + inline T& last() + { + ASSERT(m_top); + return m_data[m_top - 1]; + } inline bool isEmpty() { @@ -150,7 +153,14 @@ namespace JSC { ASSERT(0 == (size % MarkStack::pageSize())); if (size == m_allocated) return; +#if PLATFORM(WIN) || PLATFORM(SYMBIAN) + // We cannot release a part of a region with VirtualFree. To get around this, + // we'll release the entire region and reallocate the size that we want. + releaseStack(m_data, m_allocated); + m_data = reinterpret_cast<T*>(allocateStack(size)); +#else releaseStack(reinterpret_cast<char*>(m_data) + size, m_allocated - size); +#endif m_allocated = size; m_capacity = m_allocated / sizeof(T); } @@ -162,9 +172,15 @@ namespace JSC { T* m_data; }; + void* m_jsArrayVPtr; MarkStackArray<MarkSet> m_markSets; MarkStackArray<JSCell*> m_values; static size_t s_pageSize; + +#ifndef NDEBUG + public: + bool m_isCheckingForDefaultMarkViolation; +#endif }; } diff --git a/JavaScriptCore/runtime/MarkStackSymbian.cpp b/JavaScriptCore/runtime/MarkStackSymbian.cpp new file mode 100644 index 0000000..a0ce8f6 --- /dev/null +++ b/JavaScriptCore/runtime/MarkStackSymbian.cpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "MarkStack.h" + +#include <e32hal.h> + +namespace JSC { + +void MarkStack::initializePagesize() +{ + TInt page_size; + UserHal::PageSizeInBytes(page_size); + MarkStack::s_pageSize = page_size; +} + +void* MarkStack::allocateStack(size_t size) +{ + return fastMalloc(size); +} + +void MarkStack::releaseStack(void* addr, size_t size) +{ + return fastFree(addr); +} + +} diff --git a/JavaScriptCore/runtime/MarkStackWin.cpp b/JavaScriptCore/runtime/MarkStackWin.cpp index dbc3306..1fdd06a 100644 --- a/JavaScriptCore/runtime/MarkStackWin.cpp +++ b/JavaScriptCore/runtime/MarkStackWin.cpp @@ -43,9 +43,11 @@ void* MarkStack::allocateStack(size_t size) { return VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); } -void MarkStack::releaseStack(void* addr, size_t size) +void MarkStack::releaseStack(void* addr, size_t) { - VirtualFree(addr, size, MEM_RELEASE); + // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, + // dwSize must be 0 if dwFreeType is MEM_RELEASE. + VirtualFree(addr, 0, MEM_RELEASE); } } diff --git a/JavaScriptCore/runtime/MathObject.cpp b/JavaScriptCore/runtime/MathObject.cpp index 2572bc9..e8b7b97 100644 --- a/JavaScriptCore/runtime/MathObject.cpp +++ b/JavaScriptCore/runtime/MathObject.cpp @@ -85,7 +85,7 @@ const ClassInfo MathObject::info = { "Math", 0, 0, ExecState::mathTable }; @end */ -MathObject::MathObject(ExecState* exec, PassRefPtr<Structure> structure) +MathObject::MathObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) : JSObject(structure) { putDirectWithoutTransition(Identifier(exec, "E"), jsNumber(exec, exp(1.0)), DontDelete | DontEnum | ReadOnly); @@ -103,14 +103,12 @@ MathObject::MathObject(ExecState* exec, PassRefPtr<Structure> structure) bool MathObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) { - const HashEntry* entry = ExecState::mathTable(exec)->entry(exec, propertyName); - - if (!entry) - return JSObject::getOwnPropertySlot(exec, propertyName, slot); + return getStaticFunctionSlot<JSObject>(exec, ExecState::mathTable(exec), this, propertyName, slot); +} - ASSERT(entry->attributes() & Function); - setUpStaticFunctionSlot(exec, entry, this, propertyName, slot); - return true; +bool MathObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::mathTable(exec), this, propertyName, descriptor); } // ------------------------------ Functions -------------------------------- diff --git a/JavaScriptCore/runtime/MathObject.h b/JavaScriptCore/runtime/MathObject.h index 3557d1e..fee5ec5 100644 --- a/JavaScriptCore/runtime/MathObject.h +++ b/JavaScriptCore/runtime/MathObject.h @@ -27,16 +27,17 @@ namespace JSC { class MathObject : public JSObject { public: - MathObject(ExecState*, PassRefPtr<Structure>); + MathObject(ExecState*, NonNullPassRefPtr<Structure>); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; static PassRefPtr<Structure> createStructure(JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType)); + return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark | HasDefaultGetPropertyNames)); } }; diff --git a/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/JavaScriptCore/runtime/NativeErrorConstructor.cpp index 0205fc5..c655fae 100644 --- a/JavaScriptCore/runtime/NativeErrorConstructor.cpp +++ b/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -32,7 +32,7 @@ ASSERT_CLASS_FITS_IN_CELL(NativeErrorConstructor); const ClassInfo NativeErrorConstructor::info = { "Function", &InternalFunction::info, 0, 0 }; -NativeErrorConstructor::NativeErrorConstructor(ExecState* exec, PassRefPtr<Structure> structure, NativeErrorPrototype* nativeErrorPrototype) +NativeErrorConstructor::NativeErrorConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, NativeErrorPrototype* nativeErrorPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, nativeErrorPrototype->getDirect(exec->propertyNames().name).getString())) , m_errorStructure(ErrorInstance::createStructure(nativeErrorPrototype)) { diff --git a/JavaScriptCore/runtime/NativeErrorConstructor.h b/JavaScriptCore/runtime/NativeErrorConstructor.h index 118d1f4..152dbac 100644 --- a/JavaScriptCore/runtime/NativeErrorConstructor.h +++ b/JavaScriptCore/runtime/NativeErrorConstructor.h @@ -31,7 +31,7 @@ namespace JSC { class NativeErrorConstructor : public InternalFunction { public: - NativeErrorConstructor(ExecState*, PassRefPtr<Structure>, NativeErrorPrototype*); + NativeErrorConstructor(ExecState*, NonNullPassRefPtr<Structure>, NativeErrorPrototype*); static const ClassInfo info; diff --git a/JavaScriptCore/runtime/NativeErrorPrototype.cpp b/JavaScriptCore/runtime/NativeErrorPrototype.cpp index 84190a0..aa46a6a 100644 --- a/JavaScriptCore/runtime/NativeErrorPrototype.cpp +++ b/JavaScriptCore/runtime/NativeErrorPrototype.cpp @@ -29,7 +29,7 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(NativeErrorPrototype); -NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, PassRefPtr<Structure> structure, const UString& name, const UString& message) +NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, const UString& name, const UString& message) : JSObject(structure) { putDirect(exec->propertyNames().name, jsString(exec, name), 0); diff --git a/JavaScriptCore/runtime/NativeErrorPrototype.h b/JavaScriptCore/runtime/NativeErrorPrototype.h index 77bfe8a..0c65a9c 100644 --- a/JavaScriptCore/runtime/NativeErrorPrototype.h +++ b/JavaScriptCore/runtime/NativeErrorPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class NativeErrorPrototype : public JSObject { public: - NativeErrorPrototype(ExecState*, PassRefPtr<Structure>, const UString& name, const UString& message); + NativeErrorPrototype(ExecState*, NonNullPassRefPtr<Structure>, const UString& name, const UString& message); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/NumberConstructor.cpp b/JavaScriptCore/runtime/NumberConstructor.cpp index 2840bf0..cc6c51d 100644 --- a/JavaScriptCore/runtime/NumberConstructor.cpp +++ b/JavaScriptCore/runtime/NumberConstructor.cpp @@ -53,7 +53,7 @@ const ClassInfo NumberConstructor::info = { "Function", &InternalFunction::info, @end */ -NumberConstructor::NumberConstructor(ExecState* exec, PassRefPtr<Structure> structure, NumberPrototype* numberPrototype) +NumberConstructor::NumberConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, NumberPrototype* numberPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, numberPrototype->info.className)) { // Number.Prototype @@ -68,6 +68,11 @@ bool NumberConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& pr return getStaticValueSlot<NumberConstructor, InternalFunction>(exec, ExecState::numberTable(exec), this, propertyName, slot); } +bool NumberConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticValueDescriptor<NumberConstructor, InternalFunction>(exec, ExecState::numberTable(exec), this, propertyName, descriptor); +} + static JSValue numberConstructorNaNValue(ExecState* exec, const Identifier&, const PropertySlot&) { return jsNaN(exec); diff --git a/JavaScriptCore/runtime/NumberConstructor.h b/JavaScriptCore/runtime/NumberConstructor.h index b1224ec..908c55f 100644 --- a/JavaScriptCore/runtime/NumberConstructor.h +++ b/JavaScriptCore/runtime/NumberConstructor.h @@ -29,16 +29,17 @@ namespace JSC { class NumberConstructor : public InternalFunction { public: - NumberConstructor(ExecState*, PassRefPtr<Structure>, NumberPrototype*); + NumberConstructor(ExecState*, NonNullPassRefPtr<Structure>, NumberPrototype*); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); JSValue getValueProperty(ExecState*, int token) const; static const ClassInfo info; static PassRefPtr<Structure> createStructure(JSValue proto) { - return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance)); + return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasDefaultMark | HasDefaultGetPropertyNames)); } enum { NaNValue, NegInfinity, PosInfinity, MaxValue, MinValue }; diff --git a/JavaScriptCore/runtime/NumberObject.cpp b/JavaScriptCore/runtime/NumberObject.cpp index 0e8df17..1a7e44c 100644 --- a/JavaScriptCore/runtime/NumberObject.cpp +++ b/JavaScriptCore/runtime/NumberObject.cpp @@ -31,7 +31,7 @@ ASSERT_CLASS_FITS_IN_CELL(NumberObject); const ClassInfo NumberObject::info = { "Number", 0, 0, 0 }; -NumberObject::NumberObject(PassRefPtr<Structure> structure) +NumberObject::NumberObject(NonNullPassRefPtr<Structure> structure) : JSWrapperObject(structure) { } diff --git a/JavaScriptCore/runtime/NumberObject.h b/JavaScriptCore/runtime/NumberObject.h index d354b9b..ca3923d 100644 --- a/JavaScriptCore/runtime/NumberObject.h +++ b/JavaScriptCore/runtime/NumberObject.h @@ -27,10 +27,20 @@ namespace JSC { class NumberObject : public JSWrapperObject { public: - explicit NumberObject(PassRefPtr<Structure>); + explicit NumberObject(NonNullPassRefPtr<Structure>); static const ClassInfo info; - +#if USE(JSVALUE32) + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultGetPropertyNames)); + } +#else + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark | HasDefaultGetPropertyNames)); + } +#endif private: virtual const ClassInfo* classInfo() const { return &info; } diff --git a/JavaScriptCore/runtime/NumberPrototype.cpp b/JavaScriptCore/runtime/NumberPrototype.cpp index 947324c..df31404 100644 --- a/JavaScriptCore/runtime/NumberPrototype.cpp +++ b/JavaScriptCore/runtime/NumberPrototype.cpp @@ -45,7 +45,7 @@ static JSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*, JSObject*, J // ECMA 15.7.4 -NumberPrototype::NumberPrototype(ExecState* exec, PassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) +NumberPrototype::NumberPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) : NumberObject(structure) { setInternalValue(jsNumber(exec, 0)); diff --git a/JavaScriptCore/runtime/NumberPrototype.h b/JavaScriptCore/runtime/NumberPrototype.h index 0a3a544..1fb2077 100644 --- a/JavaScriptCore/runtime/NumberPrototype.h +++ b/JavaScriptCore/runtime/NumberPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class NumberPrototype : public NumberObject { public: - NumberPrototype(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure); + NumberPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/NumericStrings.h b/JavaScriptCore/runtime/NumericStrings.h new file mode 100644 index 0000000..c0696a4 --- /dev/null +++ b/JavaScriptCore/runtime/NumericStrings.h @@ -0,0 +1,74 @@ +/* + * 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 NumericStrings_h +#define NumericStrings_h + +#include "UString.h" +#include <wtf/HashFunctions.h> + +namespace JSC { + + class NumericStrings { + public: + UString add(double d) + { + CacheEntry<double>& entry = lookup(d); + if (d == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = d; + entry.value = UString::from(d); + return entry.value; + } + + UString add(int i) + { + CacheEntry<int>& entry = lookup(i); + if (i == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = i; + entry.value = UString::from(i); + return entry.value; + } + + private: + static const size_t cacheSize = 64; + + template<typename T> + struct CacheEntry { + T key; + UString value; + }; + + 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<double> doubleCache[cacheSize]; + CacheEntry<int> intCache[cacheSize]; + }; + +} // namespace JSC + +#endif // NumericStrings_h diff --git a/JavaScriptCore/runtime/ObjectConstructor.cpp b/JavaScriptCore/runtime/ObjectConstructor.cpp index 70c7cd1..a456423 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -21,9 +21,13 @@ #include "config.h" #include "ObjectConstructor.h" +#include "Error.h" #include "JSFunction.h" +#include "JSArray.h" #include "JSGlobalObject.h" #include "ObjectPrototype.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.h" #include "PrototypeFunction.h" namespace JSC { @@ -31,17 +35,27 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL objectConstructorCreate(ExecState*, JSObject*, JSValue, const ArgList&); -ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object")) +ObjectConstructor::ObjectConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) +: InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object")) { // ECMA 15.2.3.1 putDirectWithoutTransition(exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); - + // no. of arguments for constructor putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); - + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum); } // ECMA 15.2.2 @@ -82,4 +96,205 @@ JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec, JSObject* return asObject(args.at(0))->prototype(); } +JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (!args.at(0).isObject()) + return throwError(exec, TypeError, "Requested property descriptor of a value that is not an object."); + UString propertyName = args.at(1).toString(exec); + if (exec->hadException()) + return jsNull(); + JSObject* object = asObject(args.at(0)); + PropertyDescriptor descriptor; + if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) + return jsUndefined(); + if (exec->hadException()) + return jsUndefined(); + + JSObject* description = constructEmptyObject(exec); + if (!descriptor.isAccessorDescriptor()) { + description->putDirect(exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); + description->putDirect(exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); + } else { + description->putDirect(exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); + description->putDirect(exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); + } + + description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); + description->putDirect(exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); + + return description; +} + +JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (!args.at(0).isObject()) + return throwError(exec, TypeError, "Requested keys of a value that is not an object."); + PropertyNameArray properties(exec); + asObject(args.at(0))->getOwnPropertyNames(exec, properties); + JSArray* keys = constructEmptyArray(exec); + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) + keys->push(exec, jsOwnedString(exec, properties[i].ustring())); + return keys; +} + +// ES5 8.10.5 ToPropertyDescriptor +static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) +{ + if (!in.isObject()) { + throwError(exec, TypeError, "Property description must be an object."); + return false; + } + JSObject* description = asObject(in); + + PropertySlot enumerableSlot; + if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { + desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); + if (exec->hadException()) + return false; + } + + PropertySlot configurableSlot; + if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { + desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); + if (exec->hadException()) + return false; + } + + JSValue value; + PropertySlot valueSlot; + if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { + desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); + if (exec->hadException()) + return false; + } + + PropertySlot writableSlot; + if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { + desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); + if (exec->hadException()) + return false; + } + + PropertySlot getSlot; + if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { + JSValue get = getSlot.getValue(exec, exec->propertyNames().get); + if (exec->hadException()) + return false; + if (!get.isUndefined()) { + CallData callData; + if (get.getCallData(callData) == CallTypeNone) { + throwError(exec, TypeError, "Getter must be a function."); + return false; + } + } else + get = JSValue(); + desc.setGetter(get); + } + + PropertySlot setSlot; + if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { + JSValue set = setSlot.getValue(exec, exec->propertyNames().set); + if (exec->hadException()) + return false; + if (!set.isUndefined()) { + CallData callData; + if (set.getCallData(callData) == CallTypeNone) { + throwError(exec, TypeError, "Setter must be a function."); + return false; + } + } else + set = JSValue(); + + desc.setSetter(set); + } + + if (!desc.isAccessorDescriptor()) + return true; + + if (desc.value()) { + throwError(exec, TypeError, "Invalid property. 'value' present on property with getter or setter."); + return false; + } + + if (desc.writablePresent()) { + throwError(exec, TypeError, "Invalid property. 'writable' present on property with getter or setter."); + return false; + } + return true; +} + +JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (!args.at(0).isObject()) + return throwError(exec, TypeError, "Properties can only be defined on Objects."); + JSObject* O = asObject(args.at(0)); + UString propertyName = args.at(1).toString(exec); + if (exec->hadException()) + return jsNull(); + PropertyDescriptor descriptor; + if (!toPropertyDescriptor(exec, args.at(2), descriptor)) + return jsNull(); + ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); + ASSERT(!exec->hadException()); + O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true); + return O; +} + +static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) +{ + PropertyNameArray propertyNames(exec); + asObject(properties)->getOwnPropertyNames(exec, propertyNames); + size_t numProperties = propertyNames.size(); + Vector<PropertyDescriptor> descriptors; + MarkedArgumentBuffer markBuffer; + for (size_t i = 0; i < numProperties; i++) { + PropertySlot slot; + JSValue prop = properties->get(exec, propertyNames[i]); + if (exec->hadException()) + return jsNull(); + PropertyDescriptor descriptor; + if (!toPropertyDescriptor(exec, prop, descriptor)) + return jsNull(); + descriptors.append(descriptor); + // Ensure we mark all the values that we're accumulating + if (descriptor.isDataDescriptor() && descriptor.value()) + markBuffer.append(descriptor.value()); + if (descriptor.isAccessorDescriptor()) { + if (descriptor.getter()) + markBuffer.append(descriptor.getter()); + if (descriptor.setter()) + markBuffer.append(descriptor.setter()); + } + } + for (size_t i = 0; i < numProperties; i++) { + object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true); + if (exec->hadException()) + return jsNull(); + } + return object; +} + +JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (!args.at(0).isObject()) + return throwError(exec, TypeError, "Properties can only be defined on Objects."); + if (!args.at(1).isObject()) + return throwError(exec, TypeError, "Property descriptor list must be an Object."); + return defineProperties(exec, asObject(args.at(0)), asObject(args.at(1))); +} + +JSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (!args.at(0).isObject() && !args.at(0).isNull()) + return throwError(exec, TypeError, "Object prototype may only be an Object or null."); + JSObject* newObject = constructEmptyObject(exec); + newObject->setPrototype(args.at(0)); + if (args.at(1).isUndefined()) + return newObject; + if (!args.at(1).isObject()) + return throwError(exec, TypeError, "Property descriptor list must be an Object."); + return defineProperties(exec, newObject, asObject(args.at(1))); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/ObjectConstructor.h b/JavaScriptCore/runtime/ObjectConstructor.h index 9373781..1d2cdde 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.h +++ b/JavaScriptCore/runtime/ObjectConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class ObjectConstructor : public InternalFunction { public: - ObjectConstructor(ExecState*, PassRefPtr<Structure>, ObjectPrototype*, Structure* prototypeFunctionStructure); + ObjectConstructor(ExecState*, NonNullPassRefPtr<Structure>, ObjectPrototype*, Structure* prototypeFunctionStructure); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/ObjectPrototype.cpp b/JavaScriptCore/runtime/ObjectPrototype.cpp index 98e4713..0970b7c 100644 --- a/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -40,8 +40,9 @@ static JSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*, JSObject*, static JSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*, JSObject*, JSValue, const ArgList&); static JSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&); -ObjectPrototype::ObjectPrototype(ExecState* exec, PassRefPtr<Structure> stucture, Structure* prototypeFunctionStructure) +ObjectPrototype::ObjectPrototype(ExecState* exec, NonNullPassRefPtr<Structure> stucture, Structure* prototypeFunctionStructure) : JSObject(stucture) + , m_hasNoPropertiesWithUInt32Names(true) { putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum); putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum); @@ -57,6 +58,24 @@ ObjectPrototype::ObjectPrototype(ExecState* exec, PassRefPtr<Structure> stucture putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().__lookupSetter__, objectProtoFuncLookupSetter), DontEnum); } +void ObjectPrototype::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSObject::put(exec, propertyName, value, slot); + + if (m_hasNoPropertiesWithUInt32Names) { + bool isUInt32; + propertyName.toStrictUInt32(&isUInt32); + m_hasNoPropertiesWithUInt32Names = !isUInt32; + } +} + +bool ObjectPrototype::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + if (m_hasNoPropertiesWithUInt32Names) + return false; + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + // ------------------------------ Functions -------------------------------- // ECMA 15.2.4.2, 15.2.4.4, 15.2.4.5, 15.2.4.7 diff --git a/JavaScriptCore/runtime/ObjectPrototype.h b/JavaScriptCore/runtime/ObjectPrototype.h index 7790ae0..489d962 100644 --- a/JavaScriptCore/runtime/ObjectPrototype.h +++ b/JavaScriptCore/runtime/ObjectPrototype.h @@ -27,7 +27,13 @@ namespace JSC { class ObjectPrototype : public JSObject { public: - ObjectPrototype(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure); + ObjectPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + + private: + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + + bool m_hasNoPropertiesWithUInt32Names; }; JSValue JSC_HOST_CALL objectProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h index c4900d3..5da9e38 100644 --- a/JavaScriptCore/runtime/Operations.h +++ b/JavaScriptCore/runtime/Operations.h @@ -308,20 +308,13 @@ namespace JSC { resultRep = UString::Rep::createEmptyBuffer(bufferSize); UString result(resultRep); - // Loop over the openards, writing them into the output buffer. + // Loop over the operands, writing them into the output buffer. for (unsigned i = 0; i < count; ++i) { JSValue v = strings[i].jsValue(); if (LIKELY(v.isString())) result.append(asString(v)->value()); - else if (v.isInt32()) - result.appendNumeric(v.asInt32()); - else { - double d; - if (v.getNumber(d)) - result.appendNumeric(d); - else - result.append(v.toString(callFrame)); - } + else + result.append(v.toString(callFrame)); } return jsString(callFrame, result); diff --git a/JavaScriptCore/runtime/PropertyDescriptor.cpp b/JavaScriptCore/runtime/PropertyDescriptor.cpp new file mode 100644 index 0000000..4db814f --- /dev/null +++ b/JavaScriptCore/runtime/PropertyDescriptor.cpp @@ -0,0 +1,195 @@ +/* + * 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 "PropertyDescriptor.h" + +#include "GetterSetter.h" +#include "JSObject.h" +#include "Operations.h" + +namespace JSC { +unsigned PropertyDescriptor::defaultAttributes = (DontDelete << 1) - 1; + +bool PropertyDescriptor::writable() const +{ + ASSERT(!isAccessorDescriptor()); + return !(m_attributes & ReadOnly); +} + +bool PropertyDescriptor::enumerable() const +{ + return !(m_attributes & DontEnum); +} + +bool PropertyDescriptor::configurable() const +{ + return !(m_attributes & DontDelete); +} + +bool PropertyDescriptor::isDataDescriptor() const +{ + return m_value || (m_seenAttributes & WritablePresent); +} + +bool PropertyDescriptor::isGenericDescriptor() const +{ + return !isAccessorDescriptor() && !isDataDescriptor(); +} + +bool PropertyDescriptor::isAccessorDescriptor() const +{ + return m_getter || m_setter; +} + +void PropertyDescriptor::setUndefined() +{ + m_value = jsUndefined(); + m_attributes = ReadOnly | DontDelete | DontEnum; +} + +JSValue PropertyDescriptor::getter() const +{ + ASSERT(isAccessorDescriptor()); + return m_getter; +} + +JSValue PropertyDescriptor::setter() const +{ + ASSERT(isAccessorDescriptor()); + return m_setter; +} + +void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes) +{ + ASSERT(value); + m_attributes = attributes; + if (attributes & (Getter | Setter)) { + GetterSetter* accessor = asGetterSetter(value); + m_getter = accessor->getter(); + m_setter = accessor->setter(); + ASSERT(m_getter || m_setter); + m_seenAttributes = EnumerablePresent | ConfigurablePresent; + m_attributes &= ~ReadOnly; + } else { + m_value = value; + m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent; + } +} + +void PropertyDescriptor::setAccessorDescriptor(JSValue getter, JSValue setter, unsigned attributes) +{ + ASSERT(attributes & (Getter | Setter)); + ASSERT(getter || setter); + m_attributes = attributes; + m_getter = getter; + m_setter = setter; + m_attributes &= ~ReadOnly; + m_seenAttributes = EnumerablePresent | ConfigurablePresent; +} + +void PropertyDescriptor::setWritable(bool writable) +{ + if (writable) + m_attributes &= ~ReadOnly; + else + m_attributes |= ReadOnly; + m_seenAttributes |= WritablePresent; +} + +void PropertyDescriptor::setEnumerable(bool enumerable) +{ + if (enumerable) + m_attributes &= ~DontEnum; + else + m_attributes |= DontEnum; + m_seenAttributes |= EnumerablePresent; +} + +void PropertyDescriptor::setConfigurable(bool configurable) +{ + if (configurable) + m_attributes &= ~DontDelete; + else + m_attributes |= DontDelete; + m_seenAttributes |= ConfigurablePresent; +} + +void PropertyDescriptor::setSetter(JSValue setter) +{ + m_setter = setter; + m_attributes |= Setter; + m_attributes &= ~ReadOnly; +} + +void PropertyDescriptor::setGetter(JSValue getter) +{ + m_getter = getter; + m_attributes |= Getter; + m_attributes &= ~ReadOnly; +} + +bool PropertyDescriptor::equalTo(const PropertyDescriptor& other) const +{ + if (!other.m_value == m_value || + !other.m_getter == m_getter || + !other.m_setter == m_setter) + return false; + return (!m_value || JSValue::strictEqual(other.m_value, m_value)) && + (!m_getter || JSValue::strictEqual(other.m_getter, m_getter)) && + (!m_setter || JSValue::strictEqual(other.m_setter, m_setter)) && + attributesEqual(other); +} + +bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const +{ + unsigned mismatch = other.m_attributes ^ m_attributes; + unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes; + if (sharedSeen & WritablePresent && mismatch & ReadOnly) + return false; + if (sharedSeen & ConfigurablePresent && mismatch & DontDelete) + return false; + if (sharedSeen & EnumerablePresent && mismatch & DontEnum) + return false; + return true; +} + +unsigned PropertyDescriptor::attributesWithOverride(const PropertyDescriptor& other) const +{ + unsigned mismatch = other.m_attributes ^ m_attributes; + unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes; + unsigned newAttributes = m_attributes & defaultAttributes; + if (sharedSeen & WritablePresent && mismatch & ReadOnly) + newAttributes ^= ReadOnly; + if (sharedSeen & ConfigurablePresent && mismatch & DontDelete) + newAttributes ^= DontDelete; + if (sharedSeen & EnumerablePresent && mismatch & DontEnum) + newAttributes ^= DontEnum; + return newAttributes; +} + +} diff --git a/JavaScriptCore/runtime/PropertyDescriptor.h b/JavaScriptCore/runtime/PropertyDescriptor.h new file mode 100644 index 0000000..40bec86 --- /dev/null +++ b/JavaScriptCore/runtime/PropertyDescriptor.h @@ -0,0 +1,80 @@ +/* + * 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 PropertyDescriptor_h +#define PropertyDescriptor_h + +#include "JSValue.h" + +namespace JSC { + class PropertyDescriptor { + public: + PropertyDescriptor() + : m_attributes(defaultAttributes) + , m_seenAttributes(0) + { + } + bool writable() const; + bool enumerable() const; + bool configurable() const; + bool isDataDescriptor() const; + bool isGenericDescriptor() const; + bool isAccessorDescriptor() const; + unsigned attributes() const { return m_attributes; } + JSValue value() const { return m_value; } + JSValue getter() const; + JSValue setter() const; + void setUndefined(); + void setDescriptor(JSValue value, unsigned attributes); + void setAccessorDescriptor(JSValue getter, JSValue setter, unsigned attributes); + void setWritable(bool); + void setEnumerable(bool); + void setConfigurable(bool); + void setValue(JSValue value) { m_value = value; } + void setSetter(JSValue); + void setGetter(JSValue); + bool isEmpty() const { return !(m_value || m_getter || m_setter || m_seenAttributes); } + bool writablePresent() const { return m_seenAttributes & WritablePresent; } + bool enumerablePresent() const { return m_seenAttributes & EnumerablePresent; } + bool configurablePresent() const { return m_seenAttributes & ConfigurablePresent; } + bool setterPresent() const { return m_setter; } + bool getterPresent() const { return m_getter; } + bool equalTo(const PropertyDescriptor& other) const; + bool attributesEqual(const PropertyDescriptor& other) const; + unsigned attributesWithOverride(const PropertyDescriptor& other) const; + private: + static unsigned defaultAttributes; + bool operator==(const PropertyDescriptor&){ return false; } + enum { WritablePresent = 1, EnumerablePresent = 2, ConfigurablePresent = 4}; + // May be a getter/setter + JSValue m_value; + JSValue m_getter; + JSValue m_setter; + unsigned m_attributes; + unsigned m_seenAttributes; + }; +} + +#endif diff --git a/JavaScriptCore/runtime/PropertyMapHashTable.h b/JavaScriptCore/runtime/PropertyMapHashTable.h index 44dc2b8..5b63f79 100644 --- a/JavaScriptCore/runtime/PropertyMapHashTable.h +++ b/JavaScriptCore/runtime/PropertyMapHashTable.h @@ -61,6 +61,7 @@ namespace JSC { unsigned size; unsigned keyCount; unsigned deletedSentinelCount; + unsigned anonymousSlotCount; unsigned lastIndexUsed; Vector<unsigned>* deletedOffsets; unsigned entryIndices[1]; diff --git a/JavaScriptCore/runtime/PropertyNameArray.h b/JavaScriptCore/runtime/PropertyNameArray.h index b4382f4..afcc83f 100644 --- a/JavaScriptCore/runtime/PropertyNameArray.h +++ b/JavaScriptCore/runtime/PropertyNameArray.h @@ -44,7 +44,7 @@ namespace JSC { void setCachedStructure(Structure* structure) { m_cachedStructure = structure; } Structure* cachedStructure() const { return m_cachedStructure; } - void setCachedPrototypeChain(PassRefPtr<StructureChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; } + void setCachedPrototypeChain(NonNullPassRefPtr<StructureChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; } StructureChain* cachedPrototypeChain() { return m_cachedPrototypeChain.get(); } private: diff --git a/JavaScriptCore/runtime/PropertySlot.cpp b/JavaScriptCore/runtime/PropertySlot.cpp index 36fa5d8..a0a2f48 100644 --- a/JavaScriptCore/runtime/PropertySlot.cpp +++ b/JavaScriptCore/runtime/PropertySlot.cpp @@ -23,7 +23,6 @@ #include "JSFunction.h" #include "JSGlobalObject.h" -#include "JSObject.h" namespace JSC { @@ -39,7 +38,7 @@ JSValue PropertySlot::functionGetter(ExecState* exec, const Identifier&, const P return callData.native.function(exec, slot.m_data.getterFunc, slot.slotBase(), exec->emptyList()); ASSERT(callType == CallTypeJS); // FIXME: Can this be done more efficiently using the callData? - return static_cast<JSFunction*>(slot.m_data.getterFunc)->call(exec, slot.slotBase(), exec->emptyList()); + return asFunction(slot.m_data.getterFunc)->call(exec, slot.slotBase(), exec->emptyList()); } } // namespace JSC diff --git a/JavaScriptCore/runtime/PrototypeFunction.cpp b/JavaScriptCore/runtime/PrototypeFunction.cpp index 8e3d107..38f8adb 100644 --- a/JavaScriptCore/runtime/PrototypeFunction.cpp +++ b/JavaScriptCore/runtime/PrototypeFunction.cpp @@ -40,7 +40,7 @@ PrototypeFunction::PrototypeFunction(ExecState* exec, int length, const Identifi putDirect(exec->propertyNames().length, jsNumber(exec, length), DontDelete | ReadOnly | DontEnum); } -PrototypeFunction::PrototypeFunction(ExecState* exec, PassRefPtr<Structure> prototypeFunctionStructure, int length, const Identifier& name, NativeFunction function) +PrototypeFunction::PrototypeFunction(ExecState* exec, NonNullPassRefPtr<Structure> prototypeFunctionStructure, int length, const Identifier& name, NativeFunction function) : InternalFunction(&exec->globalData(), prototypeFunctionStructure, name) , m_function(function) { diff --git a/JavaScriptCore/runtime/PrototypeFunction.h b/JavaScriptCore/runtime/PrototypeFunction.h index 99ab327..70ee034 100644 --- a/JavaScriptCore/runtime/PrototypeFunction.h +++ b/JavaScriptCore/runtime/PrototypeFunction.h @@ -32,7 +32,7 @@ namespace JSC { class PrototypeFunction : public InternalFunction { public: PrototypeFunction(ExecState*, int length, const Identifier&, NativeFunction); - PrototypeFunction(ExecState*, PassRefPtr<Structure>, int length, const Identifier&, NativeFunction); + PrototypeFunction(ExecState*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); private: virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/RegExpConstructor.cpp b/JavaScriptCore/runtime/RegExpConstructor.cpp index 6a8089d..dbf2d44 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -23,6 +23,7 @@ #include "RegExpConstructor.h" #include "ArrayPrototype.h" +#include "Error.h" #include "JSArray.h" #include "JSFunction.h" #include "JSString.h" @@ -111,7 +112,7 @@ struct RegExpConstructorPrivate : FastAllocBase { unsigned lastOvectorIndex : 1; }; -RegExpConstructor::RegExpConstructor(ExecState* exec, PassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) +RegExpConstructor::RegExpConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, "RegExp")) , d(new RegExpConstructorPrivate) { @@ -233,6 +234,11 @@ bool RegExpConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& pr return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, slot); } +bool RegExpConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticValueDescriptor<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, descriptor); +} + JSValue regExpConstructorDollar1(ExecState* exec, const Identifier&, const PropertySlot& slot) { return asRegExpConstructor(slot.slotBase())->getBackref(exec, 1); @@ -329,7 +335,7 @@ JSObject* constructRegExp(ExecState* exec, const ArgList& args) JSValue arg0 = args.at(0); JSValue arg1 = args.at(1); - if (arg0.isObject(&RegExpObject::info)) { + if (arg0.inherits(&RegExpObject::info)) { if (!arg1.isUndefined()) return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); return asObject(arg0); diff --git a/JavaScriptCore/runtime/RegExpConstructor.h b/JavaScriptCore/runtime/RegExpConstructor.h index 6823f3f..f8bccf4 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.h +++ b/JavaScriptCore/runtime/RegExpConstructor.h @@ -32,15 +32,16 @@ namespace JSC { class RegExpConstructor : public InternalFunction { public: - RegExpConstructor(ExecState*, PassRefPtr<Structure>, RegExpPrototype*); + RegExpConstructor(ExecState*, NonNullPassRefPtr<Structure>, RegExpPrototype*); static PassRefPtr<Structure> createStructure(JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance)); + return Structure::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance | HasDefaultMark | HasDefaultGetPropertyNames)); } virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); static const ClassInfo info; diff --git a/JavaScriptCore/runtime/RegExpMatchesArray.h b/JavaScriptCore/runtime/RegExpMatchesArray.h index 9ae18b9..829f7cf 100644 --- a/JavaScriptCore/runtime/RegExpMatchesArray.h +++ b/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -44,6 +44,13 @@ namespace JSC { return JSArray::getOwnPropertySlot(exec, propertyName, slot); } + virtual bool getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + if (lazyCreationData()) + fillArrayInstance(exec); + return JSArray::getOwnPropertyDescriptor(exec, propertyName, descriptor); + } + virtual void put(ExecState* exec, const Identifier& propertyName, JSValue v, PutPropertySlot& slot) { if (lazyCreationData()) @@ -72,11 +79,11 @@ namespace JSC { return JSArray::deleteProperty(exec, propertyName); } - virtual void getPropertyNames(ExecState* exec, PropertyNameArray& arr) + virtual void getOwnPropertyNames(ExecState* exec, PropertyNameArray& arr) { if (lazyCreationData()) fillArrayInstance(exec); - JSArray::getPropertyNames(exec, arr); + JSArray::getOwnPropertyNames(exec, arr); } void fillArrayInstance(ExecState*); diff --git a/JavaScriptCore/runtime/RegExpObject.cpp b/JavaScriptCore/runtime/RegExpObject.cpp index 687844e..877d7b6 100644 --- a/JavaScriptCore/runtime/RegExpObject.cpp +++ b/JavaScriptCore/runtime/RegExpObject.cpp @@ -57,7 +57,7 @@ const ClassInfo RegExpObject::info = { "RegExp", 0, 0, ExecState::regExpTable }; @end */ -RegExpObject::RegExpObject(PassRefPtr<Structure> structure, PassRefPtr<RegExp> regExp) +RegExpObject::RegExpObject(NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<RegExp> regExp) : JSObject(structure) , d(new RegExpObjectData(regExp, 0)) { @@ -72,6 +72,11 @@ bool RegExpObject::getOwnPropertySlot(ExecState* exec, const Identifier& propert return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), this, propertyName, slot); } +bool RegExpObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), this, propertyName, descriptor); +} + JSValue regExpObjectGlobal(ExecState*, const Identifier&, const PropertySlot& slot) { return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->global()); diff --git a/JavaScriptCore/runtime/RegExpObject.h b/JavaScriptCore/runtime/RegExpObject.h index e83e0ac..f5a9340 100644 --- a/JavaScriptCore/runtime/RegExpObject.h +++ b/JavaScriptCore/runtime/RegExpObject.h @@ -28,7 +28,7 @@ namespace JSC { class RegExpObject : public JSObject { public: - RegExpObject(PassRefPtr<Structure>, PassRefPtr<RegExp>); + RegExpObject(NonNullPassRefPtr<Structure>, NonNullPassRefPtr<RegExp>); virtual ~RegExpObject(); void setRegExp(PassRefPtr<RegExp> r) { d->regExp = r; } @@ -41,6 +41,7 @@ namespace JSC { JSValue exec(ExecState*, const ArgList&); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual const ClassInfo* classInfo() const { return &info; } @@ -48,7 +49,7 @@ namespace JSC { static PassRefPtr<Structure> createStructure(JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType)); + return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark | HasDefaultGetPropertyNames)); } private: @@ -57,7 +58,7 @@ namespace JSC { virtual CallType getCallData(CallData&); struct RegExpObjectData : FastAllocBase { - RegExpObjectData(PassRefPtr<RegExp> regExp, double lastIndex) + RegExpObjectData(NonNullPassRefPtr<RegExp> regExp, double lastIndex) : regExp(regExp) , lastIndex(lastIndex) { diff --git a/JavaScriptCore/runtime/RegExpPrototype.cpp b/JavaScriptCore/runtime/RegExpPrototype.cpp index b1ab889..bbc9e85 100644 --- a/JavaScriptCore/runtime/RegExpPrototype.cpp +++ b/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -22,6 +22,7 @@ #include "RegExpPrototype.h" #include "ArrayPrototype.h" +#include "Error.h" #include "JSArray.h" #include "JSFunction.h" #include "JSObject.h" @@ -45,7 +46,7 @@ static JSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*, JSObject*, JSVa const ClassInfo RegExpPrototype::info = { "RegExpPrototype", 0, 0, 0 }; -RegExpPrototype::RegExpPrototype(ExecState* exec, PassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) +RegExpPrototype::RegExpPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) : JSObject(structure) { putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().compile, regExpProtoFuncCompile), DontEnum); @@ -58,28 +59,28 @@ RegExpPrototype::RegExpPrototype(ExecState* exec, PassRefPtr<Structure> structur JSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&RegExpObject::info)) + if (!thisValue.inherits(&RegExpObject::info)) return throwError(exec, TypeError); return asRegExpObject(thisValue)->test(exec, args); } JSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&RegExpObject::info)) + if (!thisValue.inherits(&RegExpObject::info)) return throwError(exec, TypeError); return asRegExpObject(thisValue)->exec(exec, args); } JSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { - if (!thisValue.isObject(&RegExpObject::info)) + if (!thisValue.inherits(&RegExpObject::info)) return throwError(exec, TypeError); RefPtr<RegExp> regExp; JSValue arg0 = args.at(0); JSValue arg1 = args.at(1); - if (arg0.isObject(&RegExpObject::info)) { + if (arg0.inherits(&RegExpObject::info)) { if (!arg1.isUndefined()) return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); regExp = asRegExpObject(arg0)->regExp(); @@ -99,8 +100,8 @@ JSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec, JSObject*, JSValue JSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { - if (!thisValue.isObject(&RegExpObject::info)) { - if (thisValue.isObject(&RegExpPrototype::info)) + if (!thisValue.inherits(&RegExpObject::info)) { + if (thisValue.inherits(&RegExpPrototype::info)) return jsNontrivialString(exec, "//"); return throwError(exec, TypeError); } diff --git a/JavaScriptCore/runtime/RegExpPrototype.h b/JavaScriptCore/runtime/RegExpPrototype.h index f5db720..d3979bd 100644 --- a/JavaScriptCore/runtime/RegExpPrototype.h +++ b/JavaScriptCore/runtime/RegExpPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class RegExpPrototype : public JSObject { public: - RegExpPrototype(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure); + RegExpPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; diff --git a/JavaScriptCore/runtime/ScopeChain.cpp b/JavaScriptCore/runtime/ScopeChain.cpp index 5c2edab..981794b 100644 --- a/JavaScriptCore/runtime/ScopeChain.cpp +++ b/JavaScriptCore/runtime/ScopeChain.cpp @@ -36,8 +36,8 @@ void ScopeChainNode::print() const ScopeChainIterator scopeEnd = end(); for (ScopeChainIterator scopeIter = begin(); scopeIter != scopeEnd; ++scopeIter) { JSObject* o = *scopeIter; - PropertyNameArray propertyNames(globalObject()->globalExec()); - o->getPropertyNames(globalObject()->globalExec(), propertyNames); + PropertyNameArray propertyNames(globalObject->globalExec()); + o->getPropertyNames(globalObject->globalExec(), propertyNames); PropertyNameArray::const_iterator propEnd = propertyNames.end(); fprintf(stderr, "----- [scope %p] -----\n", o); @@ -56,7 +56,7 @@ int ScopeChain::localDepth() const int scopeDepth = 0; ScopeChainIterator iter = this->begin(); ScopeChainIterator end = this->end(); - while (!(*iter)->isObject(&JSActivation::info)) { + while (!(*iter)->inherits(&JSActivation::info)) { ++iter; if (iter == end) break; diff --git a/JavaScriptCore/runtime/ScopeChain.h b/JavaScriptCore/runtime/ScopeChain.h index c5e16c9..0b15b67 100644 --- a/JavaScriptCore/runtime/ScopeChain.h +++ b/JavaScriptCore/runtime/ScopeChain.h @@ -33,14 +33,16 @@ namespace JSC { class ScopeChainNode : public FastAllocBase { public: - ScopeChainNode(ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSObject* globalThis) + ScopeChainNode(ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) : next(next) , object(object) , globalData(globalData) + , globalObject(globalObject) , globalThis(globalThis) , refCount(1) { ASSERT(globalData); + ASSERT(globalObject); } #ifndef NDEBUG // Due to the number of subtle and timing dependent bugs that have occurred due @@ -51,6 +53,7 @@ namespace JSC { next = 0; object = 0; globalData = 0; + globalObject = 0; globalThis = 0; } #endif @@ -58,6 +61,7 @@ namespace JSC { ScopeChainNode* next; JSObject* object; JSGlobalData* globalData; + JSGlobalObject* globalObject; JSObject* globalThis; int refCount; @@ -82,9 +86,6 @@ namespace JSC { ScopeChainIterator begin() const; ScopeChainIterator end() const; - JSGlobalObject* globalObject() const; // defined in JSGlobalObject.h - JSObject* globalThisObject() const { return globalThis; } - #ifndef NDEBUG void print() const; #endif @@ -93,7 +94,7 @@ namespace JSC { inline ScopeChainNode* ScopeChainNode::push(JSObject* o) { ASSERT(o); - return new ScopeChainNode(this, o, globalData, globalThis); + return new ScopeChainNode(this, o, globalData, globalObject, globalThis); } inline ScopeChainNode* ScopeChainNode::pop() @@ -163,8 +164,8 @@ namespace JSC { { } - ScopeChain(JSObject* o, JSGlobalData* globalData, JSObject* globalThis) - : m_node(new ScopeChainNode(0, o, globalData, globalThis)) + ScopeChain(JSObject* o, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) + : m_node(new ScopeChainNode(0, o, globalData, globalObject, globalThis)) { } @@ -203,7 +204,7 @@ namespace JSC { void pop() { m_node = m_node->pop(); } void clear() { m_node->deref(); m_node = 0; } - JSGlobalObject* globalObject() const { return m_node->globalObject(); } + JSGlobalObject* globalObject() const { return m_node->globalObject; } void markAggregate(MarkStack&) const; diff --git a/JavaScriptCore/runtime/SmallStrings.cpp b/JavaScriptCore/runtime/SmallStrings.cpp index 2f92cc1..04701cb 100644 --- a/JavaScriptCore/runtime/SmallStrings.cpp +++ b/JavaScriptCore/runtime/SmallStrings.cpp @@ -82,13 +82,13 @@ SmallStrings::~SmallStrings() { } -void SmallStrings::mark() +void SmallStrings::markChildren(MarkStack& markStack) { - if (m_emptyString && !m_emptyString->marked()) - m_emptyString->markCellDirect(); + if (m_emptyString) + markStack.append(m_emptyString); for (unsigned i = 0; i < numCharactersToStore; ++i) { - if (m_singleCharacterStrings[i] && !m_singleCharacterStrings[i]->marked()) - m_singleCharacterStrings[i]->markCellDirect(); + if (m_singleCharacterStrings[i]) + markStack.append(m_singleCharacterStrings[i]); } } diff --git a/JavaScriptCore/runtime/SmallStrings.h b/JavaScriptCore/runtime/SmallStrings.h index f0dd8df..efecbb0 100644 --- a/JavaScriptCore/runtime/SmallStrings.h +++ b/JavaScriptCore/runtime/SmallStrings.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 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 @@ -33,7 +33,7 @@ namespace JSC { class JSGlobalData; class JSString; - + class MarkStack; class SmallStringsStorage; class SmallStrings : public Noncopyable { @@ -56,7 +56,7 @@ namespace JSC { UString::Rep* singleCharacterStringRep(unsigned char character); - void mark(); + void markChildren(MarkStack&); unsigned count() const; diff --git a/JavaScriptCore/runtime/StringConstructor.cpp b/JavaScriptCore/runtime/StringConstructor.cpp index 6380445..2f3adbe 100644 --- a/JavaScriptCore/runtime/StringConstructor.cpp +++ b/JavaScriptCore/runtime/StringConstructor.cpp @@ -47,7 +47,7 @@ static JSValue JSC_HOST_CALL stringFromCharCode(ExecState* exec, JSObject*, JSVa ASSERT_CLASS_FITS_IN_CELL(StringConstructor); -StringConstructor::StringConstructor(ExecState* exec, PassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, StringPrototype* stringPrototype) +StringConstructor::StringConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, StringPrototype* stringPrototype) : InternalFunction(&exec->globalData(), structure, Identifier(exec, stringPrototype->classInfo()->className)) { // ECMA 15.5.3.1 String.prototype diff --git a/JavaScriptCore/runtime/StringConstructor.h b/JavaScriptCore/runtime/StringConstructor.h index 7d52c69..e511f7b 100644 --- a/JavaScriptCore/runtime/StringConstructor.h +++ b/JavaScriptCore/runtime/StringConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class StringConstructor : public InternalFunction { public: - StringConstructor(ExecState*, PassRefPtr<Structure>, Structure* prototypeFunctionStructure, StringPrototype*); + StringConstructor(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, StringPrototype*); virtual ConstructType getConstructData(ConstructData&); virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/StringObject.cpp b/JavaScriptCore/runtime/StringObject.cpp index fb44498..7216d3a 100644 --- a/JavaScriptCore/runtime/StringObject.cpp +++ b/JavaScriptCore/runtime/StringObject.cpp @@ -29,19 +29,19 @@ ASSERT_CLASS_FITS_IN_CELL(StringObject); const ClassInfo StringObject::info = { "String", 0, 0, 0 }; -StringObject::StringObject(ExecState* exec, PassRefPtr<Structure> structure) +StringObject::StringObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) : JSWrapperObject(structure) { setInternalValue(jsEmptyString(exec)); } -StringObject::StringObject(PassRefPtr<Structure> structure, JSString* string) +StringObject::StringObject(NonNullPassRefPtr<Structure> structure, JSString* string) : JSWrapperObject(structure) { setInternalValue(string); } -StringObject::StringObject(ExecState* exec, PassRefPtr<Structure> structure, const UString& string) +StringObject::StringObject(ExecState* exec, NonNullPassRefPtr<Structure> structure, const UString& string) : JSWrapperObject(structure) { setInternalValue(jsString(exec, string)); @@ -61,6 +61,13 @@ bool StringObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, Pr return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); } +bool StringObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (internalValue()->getStringPropertyDescriptor(exec, propertyName, descriptor)) + return true; + return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + void StringObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { if (propertyName == exec->propertyNames().length) @@ -75,27 +82,12 @@ bool StringObject::deleteProperty(ExecState* exec, const Identifier& propertyNam return JSObject::deleteProperty(exec, propertyName); } -void StringObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +void StringObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) { int size = internalValue()->value().size(); for (int i = 0; i < size; ++i) propertyNames.add(Identifier(exec, UString::from(i))); - return JSObject::getPropertyNames(exec, propertyNames); -} - -UString StringObject::toString(ExecState*) const -{ - return internalValue()->value(); -} - -UString StringObject::toThisString(ExecState*) const -{ - return internalValue()->value(); -} - -JSString* StringObject::toThisJSString(ExecState*) -{ - return internalValue(); + return JSObject::getOwnPropertyNames(exec, propertyNames); } } // namespace JSC diff --git a/JavaScriptCore/runtime/StringObject.h b/JavaScriptCore/runtime/StringObject.h index ea3a045..944f6ba 100644 --- a/JavaScriptCore/runtime/StringObject.h +++ b/JavaScriptCore/runtime/StringObject.h @@ -28,17 +28,18 @@ namespace JSC { class StringObject : public JSWrapperObject { public: - StringObject(ExecState*, PassRefPtr<Structure>); - StringObject(ExecState*, PassRefPtr<Structure>, const UString&); + StringObject(ExecState*, NonNullPassRefPtr<Structure>); + StringObject(ExecState*, NonNullPassRefPtr<Structure>, const UString&); static StringObject* create(ExecState*, JSString*); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState* exec, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual bool deleteProperty(ExecState*, const Identifier& propertyName); - virtual void getPropertyNames(ExecState*, PropertyNameArray&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&); virtual const ClassInfo* classInfo() const { return &info; } static const JS_EXPORTDATA ClassInfo info; @@ -51,12 +52,7 @@ namespace JSC { } protected: - StringObject(PassRefPtr<Structure>, JSString*); - - private: - virtual UString toString(ExecState*) const; - virtual UString toThisString(ExecState*) const; - virtual JSString* toThisJSString(ExecState*); + StringObject(NonNullPassRefPtr<Structure>, JSString*); }; StringObject* asStringObject(JSValue); diff --git a/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h b/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h index bc5c0a5..0cba83d 100644 --- a/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h +++ b/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h @@ -37,14 +37,14 @@ namespace JSC { } private: - StringObjectThatMasqueradesAsUndefined(ExecState* exec, PassRefPtr<Structure> structure, const UString& string) + StringObjectThatMasqueradesAsUndefined(ExecState* exec, NonNullPassRefPtr<Structure> structure, const UString& string) : StringObject(exec, structure, string) { } static PassRefPtr<Structure> createStructure(JSValue proto) { - return Structure::create(proto, TypeInfo(ObjectType, MasqueradesAsUndefined)); + return Structure::create(proto, TypeInfo(ObjectType, MasqueradesAsUndefined | HasDefaultMark)); } virtual bool toBoolean(ExecState*) const { return false; } diff --git a/JavaScriptCore/runtime/StringPrototype.cpp b/JavaScriptCore/runtime/StringPrototype.cpp index 531a302..b57732a 100644 --- a/JavaScriptCore/runtime/StringPrototype.cpp +++ b/JavaScriptCore/runtime/StringPrototype.cpp @@ -23,6 +23,8 @@ #include "StringPrototype.h" #include "CachedCall.h" +#include "Error.h" +#include "Executable.h" #include "JSArray.h" #include "JSFunction.h" #include "ObjectPrototype.h" @@ -119,7 +121,7 @@ const ClassInfo StringPrototype::info = { "String", &StringObject::info, 0, Exec */ // ECMA 15.5.4 -StringPrototype::StringPrototype(ExecState* exec, PassRefPtr<Structure> structure) +StringPrototype::StringPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure) : StringObject(exec, structure) { // The constructor will be added later, after StringConstructor has been built @@ -131,6 +133,11 @@ bool StringPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& prop return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), this, propertyName, slot); } +bool StringPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<StringObject>(exec, ExecState::stringTable(exec), this, propertyName, descriptor); +} + // ------------------------------ Functions -------------------------- static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg) @@ -220,7 +227,7 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue if (callType == CallTypeNone) replacementString = replacement.toString(exec); - if (pattern.isObject(&RegExpObject::info)) { + if (pattern.inherits(&RegExpObject::info)) { RegExp* reg = asRegExpObject(pattern)->regExp(); bool global = reg->global(); @@ -365,7 +372,7 @@ JSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec, JSObject*, JSValu if (thisValue.isString()) return thisValue; - if (thisValue.isObject(&StringObject::info)) + if (thisValue.inherits(&StringObject::info)) return asStringObject(thisValue)->internalValue(); return throwError(exec, TypeError); @@ -466,7 +473,7 @@ JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue t UString u = s; RefPtr<RegExp> reg; RegExpObject* imp = 0; - if (a0.isObject(&RegExpObject::info)) + if (a0.inherits(&RegExpObject::info)) reg = asRegExpObject(a0)->regExp(); else { /* @@ -516,7 +523,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec, JSObject*, JSValue UString u = s; RefPtr<RegExp> reg; - if (a0.isObject(&RegExpObject::info)) + if (a0.inherits(&RegExpObject::info)) reg = asRegExpObject(a0)->regExp(); else { /* @@ -568,7 +575,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t unsigned i = 0; int p0 = 0; unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec); - if (a0.isObject(&RegExpObject::info)) { + if (a0.inherits(&RegExpObject::info)) { RegExp* reg = asRegExpObject(a0)->regExp(); if (s.isEmpty() && reg->match(s, 0) >= 0) { // empty string matched by regexp -> empty array @@ -821,8 +828,8 @@ JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValu if (a0.getUInt32(smallInteger) && smallInteger <= 9) { unsigned stringSize = s.size(); unsigned bufferSize = 22 + stringSize; - UChar* buffer = static_cast<UChar*>(tryFastMalloc(bufferSize * sizeof(UChar))); - if (!buffer) + UChar* buffer; + if (!tryFastMalloc(bufferSize * sizeof(UChar)).getValue(buffer)) return jsUndefined(); buffer[0] = '<'; buffer[1] = 'f'; @@ -869,8 +876,8 @@ JSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec, JSObject*, JSValue th unsigned linkTextSize = linkText.size(); unsigned stringSize = s.size(); unsigned bufferSize = 15 + linkTextSize + stringSize; - UChar* buffer = static_cast<UChar*>(tryFastMalloc(bufferSize * sizeof(UChar))); - if (!buffer) + UChar* buffer; + if (!tryFastMalloc(bufferSize * sizeof(UChar)).getValue(buffer)) return jsUndefined(); buffer[0] = '<'; buffer[1] = 'a'; diff --git a/JavaScriptCore/runtime/StringPrototype.h b/JavaScriptCore/runtime/StringPrototype.h index 6f5344e..3a6a2a3 100644 --- a/JavaScriptCore/runtime/StringPrototype.h +++ b/JavaScriptCore/runtime/StringPrototype.h @@ -29,9 +29,10 @@ namespace JSC { class StringPrototype : public StringObject { public: - StringPrototype(ExecState*, PassRefPtr<Structure>); + StringPrototype(ExecState*, NonNullPassRefPtr<Structure>); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp index 5dfd919..7209b5f 100644 --- a/JavaScriptCore/runtime/Structure.cpp +++ b/JavaScriptCore/runtime/Structure.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 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 @@ -127,17 +127,14 @@ Structure::Structure(JSValue prototype, const TypeInfo& typeInfo) , m_propertyTable(0) , m_propertyStorageCapacity(JSObject::inlineStorageCapacity) , m_offset(noOffset) - , m_isDictionary(false) + , m_dictionaryKind(NoneDictionaryKind) , m_isPinnedPropertyTable(false) , m_hasGetterSetterProperties(false) - , m_usingSingleTransitionSlot(true) , m_attributesInPrevious(0) { ASSERT(m_prototype); ASSERT(m_prototype.isObject() || m_prototype.isNull()); - m_transitions.singleTransition = 0; - #ifndef NDEBUG #if ENABLE(JSC_MULTIPLE_THREADS) MutexLocker protect(ignoreSetMutex); @@ -156,20 +153,16 @@ Structure::Structure(JSValue prototype, const TypeInfo& typeInfo) Structure::~Structure() { if (m_previous) { - if (m_previous->m_usingSingleTransitionSlot) { - m_previous->m_transitions.singleTransition = 0; - } else { - ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious.get(), make_pair(m_attributesInPrevious, m_specificValueInPrevious)))); - m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), make_pair(m_attributesInPrevious, m_specificValueInPrevious))); - } + if (m_nameInPrevious) + m_previous->table.remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); + else + m_previous->table.removeAnonymousSlotTransition(m_anonymousSlotsInPrevious); + } if (m_cachedPropertyNameArrayData) m_cachedPropertyNameArrayData->setCachedStructure(0); - if (!m_usingSingleTransitionSlot) - delete m_transitions.table; - if (m_propertyTable) { unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; for (unsigned i = 1; i <= entryCount; i++) { @@ -279,15 +272,25 @@ void Structure::materializePropertyMap() for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) { structure = structures[i]; + if (!structure->m_nameInPrevious) { + m_propertyTable->anonymousSlotCount += structure->m_anonymousSlotsInPrevious; + continue; + } structure->m_nameInPrevious->ref(); PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed); insertIntoPropertyMapHashTable(entry); } } +void Structure::getOwnEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject) +{ + getEnumerableNamesFromPropertyTable(propertyNames); + getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames); +} + void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject) { - bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || m_isDictionary); + bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || isDictionary()); if (shouldCache && m_cachedPropertyNameArrayData) { if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) { @@ -297,12 +300,22 @@ void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& p clearEnumerationCache(); } - getEnumerableNamesFromPropertyTable(propertyNames); - getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames); + baseObject->getOwnPropertyNames(exec, propertyNames); if (m_prototype.isObject()) { propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly. - asObject(m_prototype)->getPropertyNames(exec, propertyNames); + JSObject* prototype = asObject(m_prototype); + while(1) { + if (!prototype->structure()->typeInfo().hasDefaultGetPropertyNames()) { + prototype->getPropertyNames(exec, propertyNames); + break; + } + prototype->getOwnPropertyNames(exec, propertyNames); + JSValue nextProto = prototype->prototype(); + if (!nextProto.isObject()) + break; + prototype = asObject(nextProto); + } } if (shouldCache) { @@ -336,7 +349,7 @@ void Structure::despecifyDictionaryFunction(const Identifier& propertyName) materializePropertyMapIfNecessary(); - ASSERT(m_isDictionary); + ASSERT(isDictionary()); ASSERT(m_propertyTable); unsigned i = rep->computedHash(); @@ -378,25 +391,13 @@ void Structure::despecifyDictionaryFunction(const Identifier& propertyName) PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) { - ASSERT(!structure->m_isDictionary); + ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); - if (structure->m_usingSingleTransitionSlot) { - Structure* existingTransition = structure->m_transitions.singleTransition; - if (existingTransition && existingTransition->m_nameInPrevious.get() == propertyName.ustring().rep() - && existingTransition->m_attributesInPrevious == attributes - && existingTransition->m_specificValueInPrevious == specificValue) { - - ASSERT(structure->m_transitions.singleTransition->m_offset != noOffset); - offset = structure->m_transitions.singleTransition->m_offset; - return existingTransition; - } - } else { - if (Structure* existingTransition = structure->m_transitions.table->get(make_pair(propertyName.ustring().rep(), make_pair(attributes, specificValue)))) { - ASSERT(existingTransition->m_offset != noOffset); - offset = existingTransition->m_offset; - return existingTransition; - } + if (Structure* existingTransition = structure->table.get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) { + ASSERT(existingTransition->m_offset != noOffset); + offset = existingTransition->m_offset; + return existingTransition; } return 0; @@ -404,12 +405,12 @@ PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Struct PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) { - ASSERT(!structure->m_isDictionary); + ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); if (structure->transitionCount() > s_maxTransitionLength) { - RefPtr<Structure> transition = toDictionaryTransition(structure); + RefPtr<Structure> transition = toCacheableDictionaryTransition(structure); ASSERT(structure != transition); offset = transition->put(propertyName, attributes, specificValue); if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) @@ -447,27 +448,15 @@ PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, con transition->m_offset = offset; - if (structure->m_usingSingleTransitionSlot) { - if (!structure->m_transitions.singleTransition) { - structure->m_transitions.singleTransition = transition.get(); - return transition.release(); - } - - Structure* existingTransition = structure->m_transitions.singleTransition; - structure->m_usingSingleTransitionSlot = false; - StructureTransitionTable* transitionTable = new StructureTransitionTable; - structure->m_transitions.table = transitionTable; - transitionTable->add(make_pair(existingTransition->m_nameInPrevious.get(), make_pair(existingTransition->m_attributesInPrevious, existingTransition->m_specificValueInPrevious)), existingTransition); - } - structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), make_pair(attributes, specificValue)), transition.get()); + structure->table.add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue); return transition.release(); } PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset) { - ASSERT(!structure->m_isDictionary); + ASSERT(!structure->isUncacheableDictionary()); - RefPtr<Structure> transition = toDictionaryTransition(structure); + RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure); offset = transition->remove(propertyName); @@ -509,6 +498,47 @@ PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structur return transition.release(); } +PassRefPtr<Structure> Structure::addAnonymousSlotsTransition(Structure* structure, unsigned count) +{ + if (Structure* transition = structure->table.getAnonymousSlotTransition(count)) { + ASSERT(transition->storedPrototype() == structure->storedPrototype()); + return transition; + } + ASSERT(count); + ASSERT(count < ((1<<6) - 2)); + RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo()); + + transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain; + transition->m_previous = structure; + transition->m_nameInPrevious = 0; + transition->m_attributesInPrevious = 0; + transition->m_anonymousSlotsInPrevious = count; + transition->m_specificValueInPrevious = 0; + transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; + transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; + + if (structure->m_propertyTable) { + if (structure->m_isPinnedPropertyTable) + transition->m_propertyTable = structure->copyPropertyTable(); + else { + transition->m_propertyTable = structure->m_propertyTable; + structure->m_propertyTable = 0; + } + } else { + if (structure->m_previous) + transition->materializePropertyMap(); + else + transition->createPropertyMapHashTable(); + } + + transition->addAnonymousSlots(count); + if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) + transition->growPropertyStorageCapacity(); + + structure->table.addAnonymousSlotTransition(count, transition.get()); + return transition.release(); +} + PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure) { RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo()); @@ -524,25 +554,35 @@ PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure) return transition.release(); } -PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure) +PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind) { - ASSERT(!structure->m_isDictionary); - + ASSERT(!structure->isUncacheableDictionary()); + RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo()); - transition->m_isDictionary = true; + transition->m_dictionaryKind = kind; transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; - + structure->materializePropertyMapIfNecessary(); transition->m_propertyTable = structure->copyPropertyTable(); transition->m_isPinnedPropertyTable = true; - + return transition.release(); } +PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure) +{ + return toDictionaryTransition(structure, CachedDictionaryKind); +} + +PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure) +{ + return toDictionaryTransition(structure, UncachedDictionaryKind); +} + PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure) { - ASSERT(structure->m_isDictionary); + ASSERT(structure->isDictionary()); // Since dictionary Structures are not shared, and no opcodes specialize // for them, we don't need to allocate a new Structure when transitioning @@ -551,15 +591,13 @@ PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure) // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the // deleted offsets vector) before transitioning from dictionary. if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty()) - structure->m_isDictionary = false; + structure->m_dictionaryKind = NoneDictionaryKind; return structure; } size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) { - ASSERT(!m_transitions.singleTransition); - materializePropertyMapIfNecessary(); m_isPinnedPropertyTable = true; @@ -572,8 +610,7 @@ size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, u size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName) { - ASSERT(!m_transitions.singleTransition); - ASSERT(m_isDictionary); + ASSERT(isUncacheableDictionary()); materializePropertyMapIfNecessary(); @@ -636,6 +673,7 @@ PropertyMapHashTable* Structure::copyPropertyTable() if (m_propertyTable->deletedOffsets) newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets); + newTable->anonymousSlotCount = m_propertyTable->anonymousSlotCount; return newTable; } @@ -815,7 +853,7 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel newOffset = m_propertyTable->deletedOffsets->last(); m_propertyTable->deletedOffsets->removeLast(); } else - newOffset = m_propertyTable->keyCount; + newOffset = m_propertyTable->keyCount + m_propertyTable->anonymousSlotCount; m_propertyTable->entries()[entryIndex - 1].offset = newOffset; ++m_propertyTable->keyCount; @@ -827,6 +865,16 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel return newOffset; } +void Structure::addAnonymousSlots(unsigned count) +{ + m_propertyTable->anonymousSlotCount += count; +} + +bool Structure::hasTransition(UString::Rep* rep, unsigned attributes) +{ + return table.hasTransition(make_pair(rep, attributes)); +} + size_t Structure::remove(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); @@ -980,6 +1028,7 @@ void Structure::rehashPropertyMapHashTable(unsigned newTableSize) m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); m_propertyTable->size = newTableSize; m_propertyTable->sizeMask = newTableSize - 1; + m_propertyTable->anonymousSlotCount = oldTable->anonymousSlotCount; unsigned lastIndexUsed = 0; unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount; diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h index f3a0c7c..ed9f6e5 100644 --- a/JavaScriptCore/runtime/Structure.h +++ b/JavaScriptCore/runtime/Structure.h @@ -29,11 +29,10 @@ #include "Identifier.h" #include "JSType.h" #include "JSValue.h" -#include "MarkStack.h" #include "PropertyMapHashTable.h" #include "StructureChain.h" #include "StructureTransitionTable.h" -#include "TypeInfo.h" +#include "JSTypeInfo.h" #include "UString.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -46,12 +45,14 @@ namespace JSC { + class MarkStack; class PropertyNameArray; class PropertyNameArrayData; class Structure : public RefCounted<Structure> { public: friend class JIT; + friend class StructureTransitionTable; static PassRefPtr<Structure> create(JSValue prototype, const TypeInfo& typeInfo) { return adoptRef(new Structure(prototype, typeInfo)); @@ -66,24 +67,24 @@ namespace JSC { static PassRefPtr<Structure> addPropertyTransitionToExistingStructure(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); static PassRefPtr<Structure> removePropertyTransition(Structure*, const Identifier& propertyName, size_t& offset); static PassRefPtr<Structure> changePrototypeTransition(Structure*, JSValue prototype); - static PassRefPtr<Structure> despecifyFunctionTransition(Structure*, const Identifier&); + static PassRefPtr<Structure> despecifyFunctionTransition(Structure*, const Identifier&); + static PassRefPtr<Structure> addAnonymousSlotsTransition(Structure*, unsigned count); static PassRefPtr<Structure> getterSetterTransition(Structure*); - static PassRefPtr<Structure> toDictionaryTransition(Structure*); + static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*); + static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*); static PassRefPtr<Structure> fromDictionaryTransition(Structure*); ~Structure(); - void markAggregate(MarkStack& markStack) - { - markStack.append(m_prototype); - } + void markAggregate(MarkStack&); // These should be used with caution. size_t addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue); size_t removePropertyWithoutTransition(const Identifier& propertyName); void setPrototypeWithoutTransition(JSValue prototype) { m_prototype = prototype; } - - bool isDictionary() const { return m_isDictionary; } + + bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; } + bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; } const TypeInfo& typeInfo() const { return m_typeInfo; } @@ -95,7 +96,7 @@ namespace JSC { void growPropertyStorageCapacity(); size_t propertyStorageCapacity() const { return m_propertyStorageCapacity; } - size_t propertyStorageSize() const { return m_propertyTable ? m_propertyTable->keyCount + (m_propertyTable->deletedOffsets ? m_propertyTable->deletedOffsets->size() : 0) : m_offset + 1; } + size_t propertyStorageSize() const { return m_propertyTable ? m_propertyTable->keyCount + m_propertyTable->anonymousSlotCount + (m_propertyTable->deletedOffsets ? m_propertyTable->deletedOffsets->size() : 0) : m_offset + 1; } bool isUsingInlineStorage() const; size_t get(const Identifier& propertyName); @@ -103,10 +104,20 @@ namespace JSC { size_t get(const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue) { ASSERT(!propertyName.isNull()); - return get(propertyName._ustring.rep(), attributes, specificValue); + return get(propertyName.ustring().rep(), attributes, specificValue); + } + bool transitionedFor(const JSCell* specificValue) + { + return m_specificValueInPrevious == specificValue; + } + bool hasTransition(UString::Rep*, unsigned attributes); + bool hasTransition(const Identifier& propertyName, unsigned attributes) + { + return hasTransition(propertyName._ustring.rep(), attributes); } void getEnumerablePropertyNames(ExecState*, PropertyNameArray&, JSObject*); + void getOwnEnumerablePropertyNames(ExecState*, PropertyNameArray&, JSObject*); bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; } @@ -118,9 +129,17 @@ namespace JSC { private: Structure(JSValue prototype, const TypeInfo&); + + typedef enum { + NoneDictionaryKind = 0, + CachedDictionaryKind = 1, + UncachedDictionaryKind = 2 + } DictionaryKind; + static PassRefPtr<Structure> toDictionaryTransition(Structure*, DictionaryKind); size_t put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue); size_t remove(const Identifier& propertyName); + void addAnonymousSlots(unsigned slotCount); void getEnumerableNamesFromPropertyTable(PropertyNameArray&); void getEnumerableNamesFromClassInfoTable(ExecState*, const ClassInfo*, PropertyNameArray&); @@ -166,13 +185,10 @@ namespace JSC { RefPtr<Structure> m_previous; RefPtr<UString::Rep> m_nameInPrevious; - - union { - Structure* singleTransition; - StructureTransitionTable* table; - } m_transitions; JSCell* m_specificValueInPrevious; + StructureTransitionTable table; + RefPtr<PropertyNameArrayData> m_cachedPropertyNameArrayData; PropertyMapHashTable* m_propertyTable; @@ -180,11 +196,18 @@ namespace JSC { size_t m_propertyStorageCapacity; signed char m_offset; - bool m_isDictionary : 1; + unsigned m_dictionaryKind : 2; bool m_isPinnedPropertyTable : 1; bool m_hasGetterSetterProperties : 1; - bool m_usingSingleTransitionSlot : 1; +#if COMPILER(WINSCW) + // Workaround for Symbian WINSCW compiler that cannot resolve unsigned type of the declared + // bitfield, when used as argument in make_pair() function calls in structure.ccp. + // This bitfield optimization is insignificant for the Symbian emulator target. + unsigned m_attributesInPrevious; +#else unsigned m_attributesInPrevious : 7; +#endif + unsigned m_anonymousSlotsInPrevious : 6; }; inline size_t Structure::get(const Identifier& propertyName) @@ -231,7 +254,58 @@ namespace JSC { return m_propertyTable->entries()[entryIndex - 1].offset; } } + + bool StructureTransitionTable::contains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) + { + if (usingSingleTransitionSlot()) { + Structure* existingTransition = singleTransition(); + return existingTransition && existingTransition->m_nameInPrevious.get() == key.first + && existingTransition->m_attributesInPrevious == key.second + && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0); + } + TransitionTable::iterator find = table()->find(key); + if (find == table()->end()) + return false; + + return find->second.first || find->second.second->transitionedFor(specificValue); + } + + Structure* StructureTransitionTable::get(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const + { + if (usingSingleTransitionSlot()) { + Structure* existingTransition = singleTransition(); + if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first + && existingTransition->m_attributesInPrevious == key.second + && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) + return existingTransition; + return 0; + } + + Transition transition = table()->get(key); + if (transition.second && transition.second->transitionedFor(specificValue)) + return transition.second; + return transition.first; + } + bool StructureTransitionTable::hasTransition(const StructureTransitionTableHash::Key& key) const + { + if (usingSingleTransitionSlot()) { + Structure* transition = singleTransition(); + return transition && transition->m_nameInPrevious == key.first + && transition->m_attributesInPrevious == key.second; + } + return table()->contains(key); + } + + void StructureTransitionTable::reifySingleTransition() + { + ASSERT(usingSingleTransitionSlot()); + Structure* existingTransition = singleTransition(); + TransitionTable* transitionTable = new TransitionTable; + setTransitionTable(transitionTable); + if (existingTransition) + add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); + } } // namespace JSC #endif // Structure_h diff --git a/JavaScriptCore/runtime/StructureChain.cpp b/JavaScriptCore/runtime/StructureChain.cpp index 85049b1..6e8a0ee 100644 --- a/JavaScriptCore/runtime/StructureChain.cpp +++ b/JavaScriptCore/runtime/StructureChain.cpp @@ -51,7 +51,10 @@ bool StructureChain::isCacheable() const uint32_t i = 0; while (m_vector[i]) { - if (m_vector[i++]->isDictionary()) + // Both classes of dictionary structure may change arbitrarily so we can't cache them + if (m_vector[i]->isDictionary()) + return false; + if (!m_vector[i++]->typeInfo().hasDefaultGetPropertyNames()) return false; } return true; diff --git a/JavaScriptCore/runtime/StructureTransitionTable.h b/JavaScriptCore/runtime/StructureTransitionTable.h index 804cbeb..0fa7b73 100644 --- a/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/JavaScriptCore/runtime/StructureTransitionTable.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 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 @@ -30,6 +30,8 @@ #include <wtf/HashFunctions.h> #include <wtf/HashMap.h> #include <wtf/HashTraits.h> +#include <wtf/PtrAndFlags.h> +#include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> namespace JSC { @@ -37,7 +39,7 @@ namespace JSC { class Structure; struct StructureTransitionTableHash { - typedef std::pair<RefPtr<UString::Rep>, std::pair<unsigned, JSCell*> > Key; + typedef std::pair<RefPtr<UString::Rep>, unsigned> Key; static unsigned hash(const Key& p) { return p.first->computedHash(); @@ -53,20 +55,159 @@ namespace JSC { struct StructureTransitionTableHashTraits { typedef WTF::HashTraits<RefPtr<UString::Rep> > FirstTraits; - typedef WTF::GenericHashTraits<unsigned> SecondFirstTraits; - typedef WTF::GenericHashTraits<JSCell*> SecondSecondTraits; - typedef std::pair<FirstTraits::TraitType, std::pair<SecondFirstTraits::TraitType, SecondSecondTraits::TraitType> > TraitType; + typedef WTF::GenericHashTraits<unsigned> SecondTraits; + typedef std::pair<FirstTraits::TraitType, SecondTraits::TraitType > TraitType; - static const bool emptyValueIsZero = FirstTraits::emptyValueIsZero && SecondFirstTraits::emptyValueIsZero && SecondSecondTraits::emptyValueIsZero; - static TraitType emptyValue() { return std::make_pair(FirstTraits::emptyValue(), std::make_pair(SecondFirstTraits::emptyValue(), SecondSecondTraits::emptyValue())); } + static const bool emptyValueIsZero = FirstTraits::emptyValueIsZero && SecondTraits::emptyValueIsZero; + static TraitType emptyValue() { return std::make_pair(FirstTraits::emptyValue(), SecondTraits::emptyValue()); } - static const bool needsDestruction = FirstTraits::needsDestruction || SecondFirstTraits::needsDestruction || SecondSecondTraits::needsDestruction; + static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; static void constructDeletedValue(TraitType& slot) { FirstTraits::constructDeletedValue(slot.first); } static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } }; - typedef HashMap<StructureTransitionTableHash::Key, Structure*, StructureTransitionTableHash, StructureTransitionTableHashTraits> StructureTransitionTable; + class StructureTransitionTable { + typedef std::pair<Structure*, Structure*> Transition; + struct TransitionTable : public HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> { + typedef HashMap<unsigned, Structure*> AnonymousSlotMap; + + void addSlotTransition(unsigned count, Structure* structure) + { + ASSERT(!getSlotTransition(count)); + if (!m_anonymousSlotTable) + m_anonymousSlotTable.set(new AnonymousSlotMap); + m_anonymousSlotTable->add(count, structure); + } + + void removeSlotTransition(unsigned count) + { + ASSERT(getSlotTransition(count)); + m_anonymousSlotTable->remove(count); + } + + Structure* getSlotTransition(unsigned count) + { + if (!m_anonymousSlotTable) + return 0; + + AnonymousSlotMap::iterator find = m_anonymousSlotTable->find(count); + if (find == m_anonymousSlotTable->end()) + return 0; + return find->second; + } + private: + OwnPtr<AnonymousSlotMap> m_anonymousSlotTable; + }; + public: + StructureTransitionTable() { + m_transitions.m_singleTransition.set(0); + m_transitions.m_singleTransition.setFlag(usingSingleSlot); + } + + ~StructureTransitionTable() { + if (!usingSingleTransitionSlot()) + delete table(); + } + + // The contains and get methods accept imprecise matches, so if an unspecialised transition exists + // for the given key they will consider that transition to be a match. If a specialised transition + // exists and it matches the provided specificValue, get will return the specific transition. + inline bool contains(const StructureTransitionTableHash::Key&, JSCell* specificValue); + inline Structure* get(const StructureTransitionTableHash::Key&, JSCell* specificValue) const; + inline bool hasTransition(const StructureTransitionTableHash::Key& key) const; + void remove(const StructureTransitionTableHash::Key& key, JSCell* specificValue) + { + if (usingSingleTransitionSlot()) { + ASSERT(contains(key, specificValue)); + setSingleTransition(0); + return; + } + TransitionTable::iterator find = table()->find(key); + if (!specificValue) + find->second.first = 0; + else + find->second.second = 0; + if (!find->second.first && !find->second.second) + table()->remove(find); + } + void add(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue) + { + if (usingSingleTransitionSlot()) { + if (!singleTransition()) { + setSingleTransition(structure); + return; + } + reifySingleTransition(); + } + if (!specificValue) { + TransitionTable::iterator find = table()->find(key); + if (find == table()->end()) + table()->add(key, Transition(structure, 0)); + else + find->second.first = structure; + } else { + // If we're adding a transition to a specific value, then there cannot be + // an existing transition + ASSERT(!table()->contains(key)); + table()->add(key, Transition(0, structure)); + } + } + + Structure* getAnonymousSlotTransition(unsigned count) + { + if (usingSingleTransitionSlot()) + return 0; + return table()->getSlotTransition(count); + } + + void addAnonymousSlotTransition(unsigned count, Structure* structure) + { + if (usingSingleTransitionSlot()) + reifySingleTransition(); + ASSERT(!table()->getSlotTransition(count)); + table()->addSlotTransition(count, structure); + } + + void removeAnonymousSlotTransition(unsigned count) + { + ASSERT(!usingSingleTransitionSlot()); + table()->removeSlotTransition(count); + } + private: + TransitionTable* table() const { ASSERT(!usingSingleTransitionSlot()); return m_transitions.m_table; } + Structure* singleTransition() const { + ASSERT(usingSingleTransitionSlot()); + return m_transitions.m_singleTransition.get(); + } + bool usingSingleTransitionSlot() const { return m_transitions.m_singleTransition.isFlagSet(usingSingleSlot); } + void setSingleTransition(Structure* structure) + { + ASSERT(usingSingleTransitionSlot()); + m_transitions.m_singleTransition.set(structure); + } + + void setTransitionTable(TransitionTable* table) + { + ASSERT(usingSingleTransitionSlot()); +#ifndef NDEBUG + setSingleTransition(0); +#endif + m_transitions.m_table = table; + // This implicitly clears the flag that indicates we're using a single transition + ASSERT(!usingSingleTransitionSlot()); + } + inline void reifySingleTransition(); + + enum UsingSingleSlot { + usingSingleSlot + }; + // Last bit indicates whether we are using the single transition optimisation + union { + TransitionTable* m_table; + PtrAndFlagsBase<Structure, UsingSingleSlot> m_singleTransition; + } m_transitions; + }; } // namespace JSC diff --git a/JavaScriptCore/runtime/SymbolTable.h b/JavaScriptCore/runtime/SymbolTable.h index c00f95a..f5e2669 100644 --- a/JavaScriptCore/runtime/SymbolTable.h +++ b/JavaScriptCore/runtime/SymbolTable.h @@ -121,6 +121,10 @@ namespace JSC { typedef HashMap<RefPtr<UString::Rep>, SymbolTableEntry, IdentifierRepHash, HashTraits<RefPtr<UString::Rep> >, SymbolTableIndexHashTraits> SymbolTable; + class SharedSymbolTable : public SymbolTable, public RefCounted<SharedSymbolTable> + { + }; + } // namespace JSC #endif // SymbolTable_h diff --git a/JavaScriptCore/runtime/TimeoutChecker.cpp b/JavaScriptCore/runtime/TimeoutChecker.cpp index 30ba6e9..2a056c9 100644 --- a/JavaScriptCore/runtime/TimeoutChecker.cpp +++ b/JavaScriptCore/runtime/TimeoutChecker.cpp @@ -35,18 +35,10 @@ #if PLATFORM(DARWIN) #include <mach/mach.h> -#endif - -#if HAVE(SYS_TIME_H) -#include <sys/time.h> -#endif - -#if PLATFORM(WIN_OS) +#elif PLATFORM(WIN_OS) #include <windows.h> -#endif - -#if PLATFORM(QT) -#include <QDateTime> +#else +#include "CurrentTime.h" #endif using namespace std; @@ -75,14 +67,6 @@ static inline unsigned getCPUTime() time += info.system_time.seconds * 1000 + info.system_time.microseconds / 1000; return time; -#elif HAVE(SYS_TIME_H) - // FIXME: This should probably use getrusage with the RUSAGE_THREAD flag. - struct timeval tv; - gettimeofday(&tv, 0); - return tv.tv_sec * 1000 + tv.tv_usec / 1000; -#elif PLATFORM(QT) - QDateTime t = QDateTime::currentDateTime(); - return t.toTime_t() * 1000 + t.time().msec(); #elif PLATFORM(WIN_OS) union { FILETIME fileTime; @@ -97,7 +81,8 @@ static inline unsigned getCPUTime() return userTime.fileTimeAsLong / 10000 + kernelTime.fileTimeAsLong / 10000; #else -#error Platform does not have getCurrentTime function + // FIXME: We should return the time the current thread has spent executing. + return currentTime() * 1000; #endif } diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp index 118751e..e66ca93 100644 --- a/JavaScriptCore/runtime/UString.cpp +++ b/JavaScriptCore/runtime/UString.cpp @@ -32,14 +32,17 @@ #include <ctype.h> #include <float.h> #include <limits.h> +#include <limits> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> #include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> #include <wtf/Vector.h> #include <wtf/unicode/UTF8.h> +#include <wtf/StringExtras.h> #if HAVE(STRING_H) #include <string.h> @@ -68,20 +71,20 @@ static const int minLengthToShare = 10; static inline size_t overflowIndicator() { return std::numeric_limits<size_t>::max(); } static inline size_t maxUChars() { return std::numeric_limits<size_t>::max() / sizeof(UChar); } -static inline UChar* allocChars(size_t length) +static inline PossiblyNull<UChar*> allocChars(size_t length) { ASSERT(length); if (length > maxUChars()) return 0; - return static_cast<UChar*>(tryFastMalloc(sizeof(UChar) * length)); + return tryFastMalloc(sizeof(UChar) * length); } -static inline UChar* reallocChars(UChar* buffer, size_t length) +static inline PossiblyNull<UChar*> reallocChars(UChar* buffer, size_t length) { ASSERT(length); if (length > maxUChars()) return 0; - return static_cast<UChar*>(tryFastRealloc(buffer, sizeof(UChar) * length)); + return tryFastRealloc(buffer, sizeof(UChar) * length); } static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) @@ -480,8 +483,7 @@ static inline bool expandCapacity(UString::Rep* rep, int requiredLength) if (requiredLength > base->capacity) { size_t newCapacity = expandedSize(requiredLength, base->preCapacity); UChar* oldBuf = base->buf; - base->buf = reallocChars(base->buf, newCapacity); - if (!base->buf) { + if (!reallocChars(base->buf, newCapacity).getValue(base->buf)) { base->buf = oldBuf; return false; } @@ -512,8 +514,7 @@ bool UString::Rep::reserveCapacity(int capacity) size_t newCapacity = expandedSize(capacity, base->preCapacity); UChar* oldBuf = base->buf; - base->buf = reallocChars(base->buf, newCapacity); - if (!base->buf) { + if (!reallocChars(base->buf, newCapacity).getValue(base->buf)) { base->buf = oldBuf; return false; } @@ -540,8 +541,8 @@ void UString::expandPreCapacity(int requiredPreCap) size_t newCapacity = expandedSize(requiredPreCap, base->capacity); int delta = newCapacity - base->capacity - base->preCapacity; - UChar* newBuf = allocChars(newCapacity); - if (!newBuf) { + UChar* newBuf; + if (!allocChars(newCapacity).getValue(newBuf)) { makeNull(); return; } @@ -566,8 +567,8 @@ static PassRefPtr<UString::Rep> createRep(const char* c) return &UString::Rep::empty(); size_t length = strlen(c); - UChar* d = allocChars(length); - if (!d) + UChar* d; + if (!allocChars(length).getValue(d)) return &UString::Rep::null(); else { for (size_t i = 0; i < length; i++) @@ -656,8 +657,8 @@ static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Re } else { // This is shared in some way that prevents us from modifying base, so we must make a whole new string. size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) + UChar* d; + if (!allocChars(newCapacity).getValue(d)) rep = &UString::Rep::null(); else { copyChars(d, rep->data(), thisSize); @@ -712,8 +713,8 @@ static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Re } else { // This is shared in some way that prevents us from modifying base, so we must make a whole new string. size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) + UChar* d; + if (!allocChars(newCapacity).getValue(d)) rep = &UString::Rep::null(); else { copyChars(d, rep->data(), thisSize); @@ -800,8 +801,8 @@ PassRefPtr<UString::Rep> concatenate(UString::Rep* a, UString::Rep* b) // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) + UChar* d; + if (!allocChars(newCapacity).getValue(d)) return 0; copyChars(d, a->data(), aSize); copyChars(d + aSize, b->data(), bSize); @@ -942,6 +943,39 @@ UString UString::from(int i) return UString(p, static_cast<int>(end - p)); } +UString UString::from(long long i) +{ + UChar buf[1 + sizeof(i) * 3]; + UChar* end = buf + sizeof(buf) / sizeof(UChar); + UChar* p = end; + + if (i == 0) + *--p = '0'; + else if (i == std::numeric_limits<long long>::min()) { + char minBuf[1 + sizeof(i) * 3]; +#if PLATFORM(WIN_OS) + snprintf(minBuf, sizeof(minBuf) - 1, "%I64d", std::numeric_limits<long long>::min()); +#else + snprintf(minBuf, sizeof(minBuf) - 1, "%lld", std::numeric_limits<long long>::min()); +#endif + return UString(minBuf); + } else { + bool negative = false; + if (i < 0) { + negative = true; + i = -i; + } + while (i) { + *--p = static_cast<unsigned short>((i % 10) + '0'); + i /= 10; + } + if (negative) + *--p = '-'; + } + + return UString(p, static_cast<int>(end - p)); +} + UString UString::from(unsigned int u) { UChar buf[sizeof(u) * 3]; @@ -994,6 +1028,8 @@ UString UString::from(double d) // avoid ever printing -NaN, in JS conceptually there is only one NaN value if (isnan(d)) return "NaN"; + if (!d) + return "0"; // -0 -> "0" char buf[80]; int decimalPoint; @@ -1076,8 +1112,8 @@ UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, in if (totalLength == 0) return ""; - UChar* buffer = allocChars(totalLength); - if (!buffer) + UChar* buffer; + if (!allocChars(totalLength).getValue(buffer)) return null(); int maxCount = max(rangeCount, separatorCount); @@ -1105,8 +1141,8 @@ UString UString::replaceRange(int rangeStart, int rangeLength, const UString& re if (totalLength == 0) return ""; - UChar* buffer = allocChars(totalLength); - if (!buffer) + UChar* buffer; + if (!allocChars(totalLength).getValue(buffer)) return null(); copyChars(buffer, data(), rangeStart); @@ -1153,8 +1189,8 @@ UString& UString::append(const UString &t) } else { // This is shared in some way that prevents us from modifying base, so we must make a whole new string. size_t newCapacity = expandedSize(length, 0); - UChar* d = allocChars(newCapacity); - if (!d) + UChar* d; + if (!allocChars(newCapacity).getValue(d)) makeNull(); else { copyChars(d, data(), thisSize); @@ -1176,18 +1212,6 @@ UString& UString::append(const UChar* tData, int tSize) return *this; } -UString& UString::appendNumeric(int i) -{ - m_rep = concatenate(rep(), i); - return *this; -} - -UString& UString::appendNumeric(double d) -{ - m_rep = concatenate(rep(), d); - return *this; -} - UString& UString::append(const char* t) { m_rep = concatenate(m_rep.release(), t); @@ -1206,8 +1230,8 @@ UString& UString::append(UChar c) if (length == 0) { // this is empty - must make a new m_rep because we don't want to pollute the shared empty one size_t newCapacity = expandedSize(1, 0); - UChar* d = allocChars(newCapacity); - if (!d) + UChar* d; + if (!allocChars(newCapacity).getValue(d)) makeNull(); else { d[0] = c; @@ -1234,8 +1258,8 @@ UString& UString::append(UChar c) } else { // This is shared in some way that prevents us from modifying base, so we must make a whole new string. size_t newCapacity = expandedSize(length + 1, 0); - UChar* d = allocChars(newCapacity); - if (!d) + UChar* d; + if (!allocChars(newCapacity).getValue(d)) makeNull(); else { copyChars(d, data(), length); @@ -1313,8 +1337,7 @@ UString& UString::operator=(const char* c) m_rep->_hash = 0; m_rep->len = l; } else { - d = allocChars(l); - if (!d) { + if (!allocChars(l).getValue(d)) { makeNull(); return *this; } diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h index d01b75d..c4dad2a 100644 --- a/JavaScriptCore/runtime/UString.h +++ b/JavaScriptCore/runtime/UString.h @@ -91,7 +91,8 @@ namespace JSC { { // Guard against integer overflow if (size < (std::numeric_limits<size_t>::max() / sizeof(UChar))) { - if (void * buf = tryFastMalloc(size * sizeof(UChar))) + void* buf = 0; + if (tryFastMalloc(size * sizeof(UChar)).getValue(buf)) return adoptRef(new BaseString(static_cast<UChar*>(buf), 0, size)); } return adoptRef(new BaseString(0, 0, 0)); @@ -256,6 +257,7 @@ namespace JSC { } static UString from(int); + static UString from(long long); static UString from(unsigned int); static UString from(long); static UString from(double); @@ -285,8 +287,6 @@ namespace JSC { UString& append(UChar); UString& append(char c) { return append(static_cast<UChar>(static_cast<unsigned char>(c))); } UString& append(const UChar*, int size); - UString& appendNumeric(int); - UString& appendNumeric(double); bool getCString(CStringBuffer&) const; |