diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime')
205 files changed, 34985 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/ArgList.cpp b/Source/JavaScriptCore/runtime/ArgList.cpp new file mode 100644 index 0000000..ab2b5d7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgList.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 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 + * 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 "ArgList.h" + +#include "JSValue.h" +#include "JSCell.h" + +using std::min; + +namespace JSC { + +void ArgList::getSlice(int startIndex, ArgList& result) const +{ + if (startIndex <= 0 || static_cast<unsigned>(startIndex) >= m_argCount) { + result = ArgList(m_args, 0); + return; + } + result = ArgList(m_args + startIndex, m_argCount - startIndex); +} + +void MarkedArgumentBuffer::markLists(MarkStack& markStack, ListSet& markSet) +{ + ListSet::iterator end = markSet.end(); + for (ListSet::iterator it = markSet.begin(); it != end; ++it) { + MarkedArgumentBuffer* list = *it; + markStack.appendValues(reinterpret_cast<JSValue*>(list->m_buffer), list->m_size); + } +} + +void MarkedArgumentBuffer::slowAppend(JSValue v) +{ + // As long as our size stays within our Vector's inline + // capacity, all our values are allocated on the stack, and + // therefore don't need explicit marking. Once our size exceeds + // our Vector's inline capacity, though, our values move to the + // heap, where they do need explicit marking. + if (!m_markSet) { + // We can only register for explicit marking once we know which heap + // is the current one, i.e., when a non-immediate value is appended. + if (Heap* heap = Heap::heap(v)) { + ListSet& markSet = heap->markListSet(); + markSet.add(this); + m_markSet = &markSet; + } + } + + if (m_vector.size() < m_vector.capacity()) { + m_vector.uncheckedAppend(v); + return; + } + + // 4x growth would be excessive for a normal vector, but it's OK for Lists + // because they're short-lived. + m_vector.reserveCapacity(m_vector.capacity() * 4); + + m_vector.uncheckedAppend(v); + m_buffer = m_vector.data(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArgList.h b/Source/JavaScriptCore/runtime/ArgList.h new file mode 100644 index 0000000..cd563a2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgList.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * 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 + * 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. + * + */ + +#ifndef ArgList_h +#define ArgList_h + +#include "CallFrame.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; + typedef Vector<Register, inlineCapacity> VectorType; + typedef HashSet<MarkedArgumentBuffer*> ListSet; + + public: + typedef VectorType::iterator iterator; + typedef VectorType::const_iterator const_iterator; + + // Constructor for a read-write list, to which you may append values. + // FIXME: Remove all clients of this API, then remove this API. + MarkedArgumentBuffer() + : m_isUsingInlineBuffer(true) + , m_markSet(0) +#ifndef NDEBUG + , m_isReadOnly(false) +#endif + { + m_buffer = m_vector.data(); + m_size = 0; + } + + // Constructor for a read-only list whose data has already been allocated elsewhere. + MarkedArgumentBuffer(Register* buffer, size_t size) + : m_buffer(buffer) + , m_size(size) + , m_isUsingInlineBuffer(true) + , m_markSet(0) +#ifndef NDEBUG + , m_isReadOnly(true) +#endif + { + } + + void initialize(Register* buffer, size_t size) + { + ASSERT(!m_markSet); + ASSERT(isEmpty()); + + m_buffer = buffer; + m_size = size; +#ifndef NDEBUG + m_isReadOnly = true; +#endif + } + + ~MarkedArgumentBuffer() + { + if (m_markSet) + m_markSet->remove(this); + } + + size_t size() const { return m_size; } + bool isEmpty() const { return !m_size; } + + JSValue at(size_t i) const + { + if (i < m_size) + return m_buffer[i].jsValue(); + return jsUndefined(); + } + + void clear() + { + m_vector.clear(); + m_buffer = 0; + m_size = 0; + } + + void append(JSValue v) + { + ASSERT(!m_isReadOnly); + +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif + + if (m_isUsingInlineBuffer && m_size < inlineCapacity) { + m_vector.uncheckedAppend(v); + ++m_size; + } else { + // Putting this case all in one function measurably improves + // the performance of the fast "just append to inline buffer" case. + slowAppend(v); + ++m_size; + m_isUsingInlineBuffer = false; + } + } + + void removeLast() + { + ASSERT(m_size); + m_size--; + m_vector.removeLast(); + } + + JSValue last() + { + ASSERT(m_size); + return m_buffer[m_size - 1].jsValue(); + } + + iterator begin() { return m_buffer; } + iterator end() { return m_buffer + m_size; } + + const_iterator begin() const { return m_buffer; } + const_iterator end() const { return m_buffer + m_size; } + + static void markLists(MarkStack&, ListSet&); + + private: + void slowAppend(JSValue); + + Register* m_buffer; + size_t m_size; + bool m_isUsingInlineBuffer; + + VectorType m_vector; + ListSet* m_markSet; +#ifndef NDEBUG + bool m_isReadOnly; +#endif + + private: + // Prohibits new / delete, which would break GC. + friend class JSGlobalData; + + void* operator new(size_t size) + { + return fastMalloc(size); + } + void operator delete(void* p) + { + fastFree(p); + } + + void* operator new[](size_t); + void operator delete[](void*); + + void* operator new(size_t, void*); + void operator delete(void*, size_t); + }; + + class ArgList { + friend class JIT; + public: + typedef JSValue* iterator; + typedef const JSValue* const_iterator; + + ArgList() + : m_args(0) + , m_argCount(0) + { + } + + ArgList(ExecState* exec) + : m_args(reinterpret_cast<JSValue*>(&exec[exec->hostThisRegister() + 1])) + , m_argCount(exec->argumentCount()) + { + } + + ArgList(JSValue* args, unsigned argCount) + : m_args(args) + , m_argCount(argCount) + { +#if ENABLE(JSC_ZOMBIES) + for (size_t i = 0; i < argCount; i++) + ASSERT(!m_args[i].isZombie()); +#endif + } + + ArgList(Register* args, int argCount) + : m_args(reinterpret_cast<JSValue*>(args)) + , m_argCount(argCount) + { + ASSERT(argCount >= 0); + } + + ArgList(const MarkedArgumentBuffer& args) + : m_args(reinterpret_cast<JSValue*>(const_cast<Register*>(args.begin()))) + , m_argCount(args.size()) + { + } + + JSValue at(size_t idx) const + { + if (idx < m_argCount) + return m_args[idx]; + return jsUndefined(); + } + + bool isEmpty() const { return !m_argCount; } + + size_t size() const { return m_argCount; } + + iterator begin() { return m_args; } + iterator end() { return m_args + m_argCount; } + + const_iterator begin() const { return m_args; } + const_iterator end() const { return m_args + m_argCount; } + + void getSlice(int startIndex, ArgList& result) const; + private: + JSValue* m_args; + size_t m_argCount; + }; + +} // namespace JSC + +#endif // ArgList_h diff --git a/Source/JavaScriptCore/runtime/Arguments.cpp b/Source/JavaScriptCore/runtime/Arguments.cpp new file mode 100644 index 0000000..39886a8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Arguments.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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 "Arguments.h" + +#include "JSActivation.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" + +using namespace std; + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(Arguments); + +const ClassInfo Arguments::info = { "Arguments", 0, 0, 0 }; + +Arguments::~Arguments() +{ + if (d->extraArguments != d->extraArgumentsFixedBuffer) + delete [] d->extraArguments; +} + +void Arguments::markChildren(MarkStack& markStack) +{ + JSObject::markChildren(markStack); + + if (d->registerArray) + markStack.appendValues(reinterpret_cast<JSValue*>(d->registerArray.get()), d->numParameters); + + if (d->extraArguments) { + unsigned numExtraArguments = d->numArguments - d->numParameters; + markStack.appendValues(reinterpret_cast<JSValue*>(d->extraArguments), numExtraArguments); + } + + markStack.append(d->callee); + + if (d->activation) + markStack.append(d->activation); +} + +void Arguments::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize) +{ + if (UNLIKELY(d->overrodeLength)) { + unsigned length = min(get(exec, exec->propertyNames().length).toUInt32(exec), maxSize); + for (unsigned i = 0; i < length; i++) + buffer[i] = get(exec, i); + return; + } + + if (LIKELY(!d->deletedArguments)) { + unsigned parametersLength = min(min(d->numParameters, d->numArguments), maxSize); + unsigned i = 0; + for (; i < parametersLength; ++i) + buffer[i] = d->registers[d->firstParameterIndex + i].jsValue(); + for (; i < d->numArguments; ++i) + buffer[i] = d->extraArguments[i - d->numParameters].jsValue(); + return; + } + + unsigned parametersLength = min(min(d->numParameters, d->numArguments), maxSize); + unsigned i = 0; + for (; i < parametersLength; ++i) { + if (!d->deletedArguments[i]) + buffer[i] = d->registers[d->firstParameterIndex + i].jsValue(); + else + buffer[i] = get(exec, i); + } + for (; i < d->numArguments; ++i) { + if (!d->deletedArguments[i]) + buffer[i] = d->extraArguments[i - d->numParameters].jsValue(); + else + buffer[i] = get(exec, i); + } +} + +void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) +{ + if (UNLIKELY(d->overrodeLength)) { + unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec); + for (unsigned i = 0; i < length; i++) + args.append(get(exec, i)); + return; + } + + if (LIKELY(!d->deletedArguments)) { + if (LIKELY(!d->numParameters)) { + args.initialize(d->extraArguments, d->numArguments); + return; + } + + if (d->numParameters == d->numArguments) { + args.initialize(&d->registers[d->firstParameterIndex], d->numArguments); + return; + } + + unsigned parametersLength = min(d->numParameters, d->numArguments); + unsigned i = 0; + for (; i < parametersLength; ++i) + args.append(d->registers[d->firstParameterIndex + i].jsValue()); + for (; i < d->numArguments; ++i) + args.append(d->extraArguments[i - d->numParameters].jsValue()); + return; + } + + unsigned parametersLength = min(d->numParameters, d->numArguments); + unsigned i = 0; + for (; i < parametersLength; ++i) { + if (!d->deletedArguments[i]) + args.append(d->registers[d->firstParameterIndex + i].jsValue()); + else + args.append(get(exec, i)); + } + for (; i < d->numArguments; ++i) { + if (!d->deletedArguments[i]) + args.append(d->extraArguments[i - d->numParameters].jsValue()); + else + args.append(get(exec, i)); + } +} + +bool Arguments::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) +{ + if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { + if (i < d->numParameters) { + slot.setRegisterSlot(&d->registers[d->firstParameterIndex + i]); + } else + slot.setValue(d->extraArguments[i - d->numParameters].jsValue()); + return true; + } + + return JSObject::getOwnPropertySlot(exec, Identifier(exec, UString::number(i)), slot); +} + +void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) +{ + if (d->overrodeCaller) + return; + + d->overrodeCaller = true; + PropertyDescriptor descriptor; + JSValue thrower = createTypeErrorFunction(exec, "Unable to access caller of strict mode function"); + descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); + defineOwnProperty(exec, exec->propertyNames().caller, descriptor, false); +} + +void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec) +{ + if (d->overrodeCallee) + return; + + d->overrodeCallee = true; + PropertyDescriptor descriptor; + JSValue thrower = createTypeErrorFunction(exec, "Unable to access callee of strict mode function"); + descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); + defineOwnProperty(exec, exec->propertyNames().callee, descriptor, false); +} + +bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { + if (i < d->numParameters) { + slot.setRegisterSlot(&d->registers[d->firstParameterIndex + i]); + } else + slot.setValue(d->extraArguments[i - d->numParameters].jsValue()); + return true; + } + + if (propertyName == exec->propertyNames().length && LIKELY(!d->overrodeLength)) { + slot.setValue(jsNumber(d->numArguments)); + return true; + } + + if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) { + if (!d->isStrictMode) { + slot.setValue(d->callee); + return true; + } + createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && d->isStrictMode) + createStrictModeCallerIfNecessary(exec); + + 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(d->numArguments), DontEnum); + return true; + } + + if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) { + if (!d->isStrictMode) { + descriptor.setDescriptor(d->callee, DontEnum); + return true; + } + createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && d->isStrictMode) + createStrictModeCallerIfNecessary(exec); + + return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void Arguments::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) { + for (unsigned i = 0; i < d->numArguments; ++i) { + if (!d->deletedArguments || !d->deletedArguments[i]) + propertyNames.add(Identifier(exec, UString::number(i))); + } + propertyNames.add(exec->propertyNames().callee); + propertyNames.add(exec->propertyNames().length); + } + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +void Arguments::put(ExecState* exec, unsigned i, JSValue value, PutPropertySlot& slot) +{ + if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { + if (i < d->numParameters) + d->registers[d->firstParameterIndex + i] = JSValue(value); + else + d->extraArguments[i - d->numParameters] = JSValue(value); + return; + } + + JSObject::put(exec, Identifier(exec, UString::number(i)), value, slot); +} + +void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { + if (i < d->numParameters) + d->registers[d->firstParameterIndex + i] = JSValue(value); + else + d->extraArguments[i - d->numParameters] = JSValue(value); + return; + } + + if (propertyName == exec->propertyNames().length && !d->overrodeLength) { + d->overrodeLength = true; + putDirect(propertyName, value, DontEnum); + return; + } + + if (propertyName == exec->propertyNames().callee && !d->overrodeCallee) { + if (!d->isStrictMode) { + d->overrodeCallee = true; + putDirect(propertyName, value, DontEnum); + return; + } + createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && d->isStrictMode) + createStrictModeCallerIfNecessary(exec); + + JSObject::put(exec, propertyName, value, slot); +} + +bool Arguments::deleteProperty(ExecState* exec, unsigned i) +{ + if (i < d->numArguments) { + if (!d->deletedArguments) { + d->deletedArguments.set(new bool[d->numArguments]); + memset(d->deletedArguments.get(), 0, sizeof(bool) * d->numArguments); + } + if (!d->deletedArguments[i]) { + d->deletedArguments[i] = true; + return true; + } + } + + return JSObject::deleteProperty(exec, Identifier(exec, UString::number(i))); +} + +bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < d->numArguments) { + if (!d->deletedArguments) { + d->deletedArguments.set(new bool[d->numArguments]); + memset(d->deletedArguments.get(), 0, sizeof(bool) * d->numArguments); + } + if (!d->deletedArguments[i]) { + d->deletedArguments[i] = true; + return true; + } + } + + if (propertyName == exec->propertyNames().length && !d->overrodeLength) { + d->overrodeLength = true; + return true; + } + + if (propertyName == exec->propertyNames().callee && !d->overrodeCallee) { + if (!d->isStrictMode) { + d->overrodeCallee = true; + return true; + } + createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && !d->isStrictMode) + createStrictModeCallerIfNecessary(exec); + + return JSObject::deleteProperty(exec, propertyName); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h new file mode 100644 index 0000000..715a2ac --- /dev/null +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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. + * + */ + +#ifndef Arguments_h +#define Arguments_h + +#include "JSActivation.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "Interpreter.h" +#include "ObjectConstructor.h" +#include "PrototypeFunction.h" + +namespace JSC { + + struct ArgumentsData : Noncopyable { + JSActivation* activation; + + unsigned numParameters; + ptrdiff_t firstParameterIndex; + unsigned numArguments; + + Register* registers; + OwnArrayPtr<Register> registerArray; + + Register* extraArguments; + OwnArrayPtr<bool> deletedArguments; + Register extraArgumentsFixedBuffer[4]; + + JSFunction* callee; + bool overrodeLength : 1; + bool overrodeCallee : 1; + bool overrodeCaller : 1; + bool isStrictMode : 1; + }; + + + class Arguments : public JSObject { + public: + // Use an enum because otherwise gcc insists on doing a memory + // read. + enum { MaxArguments = 0x10000 }; + + enum NoParametersType { NoParameters }; + + Arguments(CallFrame*); + Arguments(CallFrame*, NoParametersType); + virtual ~Arguments(); + + static const ClassInfo info; + + virtual void markChildren(MarkStack&); + + void fillArgList(ExecState*, MarkedArgumentBuffer&); + + uint32_t numProvidedArguments(ExecState* exec) const + { + if (UNLIKELY(d->overrodeLength)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return d->numArguments; + } + + void copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize); + void copyRegisters(); + bool isTornOff() const { return d->registerArray; } + void setActivation(JSActivation* activation) + { + d->activation = activation; + d->registers = &activation->registerAt(0); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + private: + 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 getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual void put(ExecState*, unsigned propertyName, JSValue, PutPropertySlot&); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual bool deleteProperty(ExecState*, unsigned propertyName); + void createStrictModeCallerIfNecessary(ExecState*); + void createStrictModeCalleeIfNecessary(ExecState*); + + virtual const ClassInfo* classInfo() const { return &info; } + + void init(CallFrame*); + + OwnPtr<ArgumentsData> d; + }; + + Arguments* asArguments(JSValue); + + inline Arguments* asArguments(JSValue value) + { + ASSERT(asObject(value)->inherits(&Arguments::info)); + return static_cast<Arguments*>(asObject(value)); + } + + ALWAYS_INLINE void Arguments::getArgumentsData(CallFrame* callFrame, JSFunction*& function, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc) + { + function = asFunction(callFrame->callee()); + + int numParameters = function->jsExecutable()->parameterCount(); + argc = callFrame->argumentCountIncludingThis(); + + if (argc <= numParameters) + argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters; + else + argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters - argc; + + argc -= 1; // - 1 to skip "this" + firstParameterIndex = -RegisterFile::CallFrameHeaderSize - numParameters; + } + + inline Arguments::Arguments(CallFrame* callFrame) + : JSObject(callFrame->lexicalGlobalObject()->argumentsStructure()) + , d(adoptPtr(new ArgumentsData)) + { + JSFunction* callee; + ptrdiff_t firstParameterIndex; + Register* argv; + int numArguments; + getArgumentsData(callFrame, callee, firstParameterIndex, argv, numArguments); + + d->numParameters = callee->jsExecutable()->parameterCount(); + d->firstParameterIndex = firstParameterIndex; + d->numArguments = numArguments; + + d->activation = 0; + d->registers = callFrame->registers(); + + Register* extraArguments; + if (d->numArguments <= d->numParameters) + extraArguments = 0; + else { + unsigned numExtraArguments = d->numArguments - d->numParameters; + if (numExtraArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(Register)) + extraArguments = new Register[numExtraArguments]; + else + extraArguments = d->extraArgumentsFixedBuffer; + for (unsigned i = 0; i < numExtraArguments; ++i) + extraArguments[i] = argv[d->numParameters + i]; + } + + d->extraArguments = extraArguments; + + d->callee = callee; + d->overrodeLength = false; + d->overrodeCallee = false; + d->overrodeCaller = false; + d->isStrictMode = callFrame->codeBlock()->isStrictMode(); + if (d->isStrictMode) + copyRegisters(); + } + + inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) + : JSObject(callFrame->lexicalGlobalObject()->argumentsStructure()) + , d(adoptPtr(new ArgumentsData)) + { + ASSERT(!asFunction(callFrame->callee())->jsExecutable()->parameterCount()); + + unsigned numArguments = callFrame->argumentCount(); + + d->numParameters = 0; + d->numArguments = numArguments; + d->activation = 0; + + Register* extraArguments; + if (numArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(Register)) + extraArguments = new Register[numArguments]; + else + extraArguments = d->extraArgumentsFixedBuffer; + + Register* argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numArguments - 1; + for (unsigned i = 0; i < numArguments; ++i) + extraArguments[i] = argv[i]; + + d->extraArguments = extraArguments; + + d->callee = asFunction(callFrame->callee()); + d->overrodeLength = false; + d->overrodeCallee = false; + d->overrodeCaller = false; + d->isStrictMode = callFrame->codeBlock()->isStrictMode(); + if (d->isStrictMode) + copyRegisters(); + } + + inline void Arguments::copyRegisters() + { + ASSERT(!isTornOff()); + + if (!d->numParameters) + return; + + int registerOffset = d->numParameters + RegisterFile::CallFrameHeaderSize; + size_t registerArraySize = d->numParameters; + + Register* registerArray = new Register[registerArraySize]; + memcpy(registerArray, d->registers - registerOffset, registerArraySize * sizeof(Register)); + d->registerArray.set(registerArray); + d->registers = registerArray + registerOffset; + } + + // This JSActivation function is defined here so it can get at Arguments::setRegisters. + inline void JSActivation::copyRegisters() + { + ASSERT(!d()->registerArray); + + size_t numParametersMinusThis = d()->functionExecutable->parameterCount(); + size_t numVars = d()->functionExecutable->capturedVariableCount(); + size_t numLocals = numVars + numParametersMinusThis; + + if (!numLocals) + return; + + int registerOffset = numParametersMinusThis + RegisterFile::CallFrameHeaderSize; + size_t registerArraySize = numLocals + RegisterFile::CallFrameHeaderSize; + + Register* registerArray = copyRegisterArray(d()->registers - registerOffset, registerArraySize); + setRegisters(registerArray + registerOffset, registerArray); + } + +} // namespace JSC + +#endif // Arguments_h diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp new file mode 100644 index 0000000..632d466 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "ArrayConstructor.h" + +#include "ArrayPrototype.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "Lookup.h" +#include "PrototypeFunction.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ArrayConstructor); + +static EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*); + +ArrayConstructor::ArrayConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ArrayPrototype* arrayPrototype, Structure* prototypeFunctionStructure) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, arrayPrototype->classInfo()->className)) +{ + // ECMA 15.4.3.1 Array.prototype + putDirectWithoutTransition(exec->propertyNames().prototype, arrayPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); + + // ES5 + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().isArray, arrayConstructorIsArray), DontEnum); +} + +static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) +{ + // a single numeric argument denotes the array size (!) + if (args.size() == 1 && args.at(0).isNumber()) { + uint32_t n = args.at(0).toUInt32(exec); + if (n != args.at(0).toNumber(exec)) + return throwError(exec, createRangeError(exec, "Array size is not a small enough positive integer.")); + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), n, CreateInitialized); + } + + // otherwise the array is constructed with the arguments in it + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), args); +} + +static EncodedJSValue JSC_HOST_CALL constructWithArrayConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); +} + +// ECMA 15.4.2 +ConstructType ArrayConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithArrayConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callArrayConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); +} + +// ECMA 15.6.1 +CallType ArrayConstructor::getCallData(CallData& callData) +{ + // equivalent to 'new Array(....)' + callData.native.function = callArrayConstructor; + return CallTypeHost; +} + +EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState* exec) +{ + return JSValue::encode(jsBoolean(exec->argument(0).inherits(&JSArray::info))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.h b/Source/JavaScriptCore/runtime/ArrayConstructor.h new file mode 100644 index 0000000..5e1408f --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ArrayConstructor_h +#define ArrayConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class ArrayPrototype; + + class ArrayConstructor : public InternalFunction { + public: + ArrayConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, ArrayPrototype*, Structure*); + + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + +} // namespace JSC + +#endif // ArrayConstructor_h diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp new file mode 100644 index 0000000..ab0c3d4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -0,0 +1,1098 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "ArrayPrototype.h" + +#include "CachedCall.h" +#include "CodeBlock.h" +#include "Interpreter.h" +#include "JIT.h" +#include "JSStringBuilder.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "Operations.h" +#include <algorithm> +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype); + +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*); + +} + +#include "ArrayPrototype.lut.h" + +namespace JSC { + +static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData) +{ + if (callType != CallTypeJS) + return false; + + FunctionExecutable* executable = callData.js.functionExecutable; + + JSObject* error = executable->compileForCall(exec, callData.js.scopeChain); + if (error) + return false; + + return executable->generatedBytecodeForCall().isNumericCompareFunction(); +} + +// ------------------------------ ArrayPrototype ---------------------------- + +const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::arrayTable}; + +/* Source for ArrayPrototype.lut.h +@begin arrayTable 16 + toString arrayProtoFuncToString DontEnum|Function 0 + toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0 + concat arrayProtoFuncConcat DontEnum|Function 1 + join arrayProtoFuncJoin DontEnum|Function 1 + pop arrayProtoFuncPop DontEnum|Function 0 + push arrayProtoFuncPush DontEnum|Function 1 + reverse arrayProtoFuncReverse DontEnum|Function 0 + shift arrayProtoFuncShift DontEnum|Function 0 + slice arrayProtoFuncSlice DontEnum|Function 2 + sort arrayProtoFuncSort DontEnum|Function 1 + splice arrayProtoFuncSplice DontEnum|Function 2 + unshift arrayProtoFuncUnShift DontEnum|Function 1 + every arrayProtoFuncEvery DontEnum|Function 1 + forEach arrayProtoFuncForEach DontEnum|Function 1 + some arrayProtoFuncSome DontEnum|Function 1 + indexOf arrayProtoFuncIndexOf DontEnum|Function 1 + lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1 + filter arrayProtoFuncFilter DontEnum|Function 1 + reduce arrayProtoFuncReduce DontEnum|Function 1 + reduceRight arrayProtoFuncReduceRight DontEnum|Function 1 + map arrayProtoFuncMap DontEnum|Function 1 +@end +*/ + +// ECMA 15.4.4 +ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSArray(structure) +{ + putAnonymousValue(0, globalObject); +} + +bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + 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 +static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index) +{ + PropertySlot slot(obj); + if (!obj->getPropertySlot(exec, index, slot)) + return JSValue(); + return slot.getValue(exec, index); +} + +static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValue value) +{ + PutPropertySlot slot; + obj->put(exec, propertyName, value, slot); +} + +static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0) +{ + JSValue value = exec->argument(argument); + if (value.isUndefined()) + return undefinedValue; + + double indexDouble = value.toInteger(exec); + if (indexDouble < 0) { + indexDouble += length; + return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble); + } + return indexDouble > length ? length : static_cast<unsigned>(indexDouble); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + bool isRealArray = isJSArray(&exec->globalData(), thisValue); + if (!isRealArray && !thisValue.inherits(&JSArray::info)) + return throwVMTypeError(exec); + JSArray* thisObj = asArray(thisValue); + + HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; + if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { + if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) + return throwVMError(exec, createStackOverflowError(exec)); + } + + bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; + if (alreadyVisited) + return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion. + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned totalSize = length ? length - 1 : 0; +#if OS(SYMBIAN) + // Symbian has very limited stack size available. + // This function could be called recursively and allocating 1K on stack here cause + // stack overflow on Symbian devices. + Vector<RefPtr<StringImpl> > strBuffer(length); +#else + Vector<RefPtr<StringImpl>, 256> strBuffer(length); +#endif + for (unsigned k = 0; k < length; 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[k] = str.impl(); + totalSize += str.length(); + + if (!strBuffer.data()) { + throwOutOfMemoryError(exec); + } + + if (exec->hadException()) + break; + } + arrayVisitedElements.remove(thisObj); + if (!totalSize) + return JSValue::encode(jsEmptyString(exec)); + Vector<UChar> buffer; + buffer.reserveCapacity(totalSize); + if (!buffer.data()) + return JSValue::encode(throwOutOfMemoryError(exec)); + + for (unsigned i = 0; i < length; i++) { + if (i) + buffer.append(','); + if (RefPtr<StringImpl> rep = strBuffer[i]) + buffer.append(rep->characters(), rep->length()); + } + ASSERT(buffer.size() == totalSize); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&JSArray::info)) + return throwVMTypeError(exec); + JSObject* thisObj = asArray(thisValue); + + HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; + if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { + if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) + return throwVMError(exec, createStackOverflowError(exec)); + } + + bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; + if (alreadyVisited) + return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion. + + JSStringBuilder strBuffer; + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + for (unsigned k = 0; k < length; k++) { + if (k >= 1) + strBuffer.append(','); + + JSValue element = thisObj->get(exec, k); + if (!element.isUndefinedOrNull()) { + JSObject* o = element.toObject(exec); + JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); + UString str; + CallData callData; + CallType callType = getCallData(conversionFunction, callData); + if (callType != CallTypeNone) + str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec); + else + str = element.toString(exec); + strBuffer.append(str); + } + } + arrayVisitedElements.remove(thisObj); + return JSValue::encode(strBuffer.build(exec)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; + if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { + if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) + return throwVMError(exec, createStackOverflowError(exec)); + } + + bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; + if (alreadyVisited) + return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion. + + JSStringBuilder strBuffer; + + UString separator; + if (!exec->argument(0).isUndefined()) + separator = exec->argument(0).toString(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned k = 0; + if (isJSArray(&exec->globalData(), thisObj)) { + JSArray* array = asArray(thisObj); + + if (length) { + if (!array->canGetIndex(k)) + goto skipFirstLoop; + JSValue element = array->getIndex(k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + k++; + } + + if (separator.isNull()) { + for (; k < length; k++) { + if (!array->canGetIndex(k)) + break; + strBuffer.append(','); + JSValue element = array->getIndex(k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + } + } else { + for (; k < length; k++) { + if (!array->canGetIndex(k)) + break; + strBuffer.append(separator); + JSValue element = array->getIndex(k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + } + } + } + skipFirstLoop: + for (; k < length; k++) { + if (k >= 1) { + if (separator.isNull()) + strBuffer.append(','); + else + strBuffer.append(separator); + } + + JSValue element = thisObj->get(exec, k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + } + arrayVisitedElements.remove(thisObj); + return JSValue::encode(strBuffer.build(exec)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSArray* arr = constructEmptyArray(exec); + unsigned n = 0; + JSValue curArg = thisValue.toThisObject(exec); + size_t i = 0; + size_t argCount = exec->argumentCount(); + while (1) { + 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) { + if (JSValue v = getProperty(exec, curObject, k)) + arr->put(exec, n, v); + n++; + } + } else { + arr->put(exec, n, curArg); + n++; + } + if (i == argCount) + break; + curArg = (exec->argument(i)); + ++i; + } + arr->setLength(n); + return JSValue::encode(arr); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (isJSArray(&exec->globalData(), thisValue)) + return JSValue::encode(asArray(thisValue)->pop()); + + JSObject* thisObj = thisValue.toThisObject(exec); + JSValue result; + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (length == 0) { + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, length - 1); + thisObj->deleteProperty(exec, length - 1); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); + } + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (isJSArray(&exec->globalData(), thisValue) && exec->argumentCount() == 1) { + JSArray* array = asArray(thisValue); + array->push(exec, exec->argument(0)); + return JSValue::encode(jsNumber(array->length())); + } + + JSObject* thisObj = thisValue.toThisObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + for (unsigned n = 0; n < exec->argumentCount(); n++) + thisObj->put(exec, length + n, exec->argument(n)); + length += exec->argumentCount(); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); + return JSValue::encode(jsNumber(length)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned middle = length / 2; + + for (unsigned k = 0; k < middle; k++) { + unsigned lk1 = length - k - 1; + JSValue obj2 = getProperty(exec, thisObj, lk1); + JSValue obj = getProperty(exec, thisObj, k); + + if (obj2) + thisObj->put(exec, k, obj2); + else + thisObj->deleteProperty(exec, k); + + if (obj) + thisObj->put(exec, lk1, obj); + else + thisObj->deleteProperty(exec, lk1); + } + return JSValue::encode(thisObj); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + JSValue result; + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (length == 0) { + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, 0); + if (isJSArray(&exec->globalData(), thisObj)) + ((JSArray *)thisObj)->shiftCount(exec, 1); + else { + for (unsigned k = 1; k < length; k++) { + if (JSValue obj = getProperty(exec, thisObj, k)) + thisObj->put(exec, k - 1, obj); + else + thisObj->deleteProperty(exec, k - 1); + } + thisObj->deleteProperty(exec, length - 1); + } + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); + } + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) +{ + // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + // We return a new array + JSArray* resObj = constructEmptyArray(exec); + JSValue result = resObj; + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); + + unsigned n = 0; + for (unsigned k = begin; k < end; k++, n++) { + if (JSValue v = getProperty(exec, thisObj, k)) + resObj->put(exec, n, v); + } + resObj->setLength(n); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + + if (thisObj->classInfo() == &JSArray::info) { + if (isNumericCompareFunction(exec, callType, callData)) + asArray(thisObj)->sortNumeric(exec, function, callType, callData); + else if (callType != CallTypeNone) + asArray(thisObj)->sort(exec, function, callType, callData); + else + asArray(thisObj)->sort(exec); + return JSValue::encode(thisObj); + } + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + + if (!length) + return JSValue::encode(thisObj); + + // "Min" sort. Not the fastest, but definitely less code than heapsort + // or quicksort, and much less swapping than bubblesort/insertionsort. + for (unsigned i = 0; i < length - 1; ++i) { + JSValue iObj = thisObj->get(exec, i); + unsigned themin = i; + JSValue minObj = iObj; + for (unsigned j = i + 1; j < length; ++j) { + JSValue jObj = thisObj->get(exec, j); + double compareResult; + if (jObj.isUndefined()) + compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) + else if (minObj.isUndefined()) + compareResult = -1; + else if (callType != CallTypeNone) { + MarkedArgumentBuffer l; + l.append(jObj); + l.append(minObj); + compareResult = call(exec, function, callType, callData, exec->globalThisValue(), l).toNumber(exec); + } else + compareResult = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1; + + if (compareResult < 0) { + themin = j; + minObj = jObj; + } + } + // Swap themin and i + if (themin > i) { + thisObj->put(exec, i, minObj); + thisObj->put(exec, themin, iObj); + } + } + return JSValue::encode(thisObj); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + // 15.4.4.12 + + if (!exec->argumentCount()) + return JSValue::encode(constructEmptyArray(exec)); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + + unsigned deleteCount = length - begin; + if (exec->argumentCount() > 1) { + double deleteDouble = exec->argument(1).toInteger(exec); + if (deleteDouble < 0) + deleteCount = 0; + else if (deleteDouble > length - begin) + deleteCount = length - begin; + else + deleteCount = static_cast<unsigned>(deleteDouble); + } + + JSArray* resObj = new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), deleteCount, CreateCompact); + JSValue result = resObj; + + for (unsigned k = 0; k < deleteCount; k++) + resObj->uncheckedSetIndex(k, getProperty(exec, thisObj, k + begin)); + + resObj->setLength(deleteCount); + + unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); + if (additionalArgs != deleteCount) { + if (additionalArgs < deleteCount) { + if ((!begin) && (isJSArray(&exec->globalData(), thisObj))) + ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs); + else { + for (unsigned k = begin; k < length - deleteCount; ++k) { + if (JSValue v = getProperty(exec, thisObj, k + deleteCount)) + thisObj->put(exec, k + additionalArgs, v); + else + thisObj->deleteProperty(exec, k + additionalArgs); + } + for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) + thisObj->deleteProperty(exec, k - 1); + } + } else { + if ((!begin) && (isJSArray(&exec->globalData(), thisObj))) + ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount); + else { + for (unsigned k = length - deleteCount; k > begin; --k) { + if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1)) + thisObj->put(exec, k + additionalArgs - 1, obj); + else + thisObj->deleteProperty(exec, k + additionalArgs - 1); + } + } + } + } + for (unsigned k = 0; k < additionalArgs; ++k) + thisObj->put(exec, k + begin, exec->argument(k + 2)); + + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + // 15.4.4.13 + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned nrArgs = exec->argumentCount(); + if ((nrArgs) && (length)) { + if (isJSArray(&exec->globalData(), thisObj)) + ((JSArray *)thisObj)->unshiftCount(exec, nrArgs); + else { + for (unsigned k = length; k > 0; --k) { + if (JSValue v = getProperty(exec, thisObj, k - 1)) + thisObj->put(exec, k + nrArgs - 1, v); + else + thisObj->deleteProperty(exec, k + nrArgs - 1); + } + } + } + for (unsigned k = 0; k < nrArgs; ++k) + thisObj->put(exec, k, exec->argument(k)); + JSValue result = jsNumber(length + nrArgs); + putProperty(exec, thisObj, exec->propertyNames().length, result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); + JSArray* resultArray = constructEmptyArray(exec); + + unsigned filterIndex = 0; + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { + JSFunction* f = asFunction(function); + JSArray* array = asArray(thisObj); + CachedCall cachedCall(exec, f, 3); + for (; k < length && !exec->hadException(); ++k) { + if (!array->canGetIndex(k)) + break; + JSValue v = array->getIndex(k); + cachedCall.setThis(applyThis); + cachedCall.setArgument(0, v); + cachedCall.setArgument(1, jsNumber(k)); + cachedCall.setArgument(2, thisObj); + + JSValue result = cachedCall.call(); + if (result.toBoolean(exec)) + resultArray->put(exec, filterIndex++, v); + } + if (k == length) + return JSValue::encode(resultArray); + } + for (; k < length && !exec->hadException(); ++k) { + PropertySlot slot(thisObj); + + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + JSValue v = slot.getValue(exec, k); + + MarkedArgumentBuffer eachArguments; + + eachArguments.append(v); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); + + if (result.toBoolean(exec)) + resultArray->put(exec, filterIndex++, v); + } + return JSValue::encode(resultArray); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + + JSArray* resultArray = constructEmptyArray(exec, length); + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { + JSFunction* f = asFunction(function); + JSArray* array = asArray(thisObj); + CachedCall cachedCall(exec, f, 3); + for (; k < length && !exec->hadException(); ++k) { + if (UNLIKELY(!array->canGetIndex(k))) + break; + + cachedCall.setThis(applyThis); + cachedCall.setArgument(0, array->getIndex(k)); + cachedCall.setArgument(1, jsNumber(k)); + cachedCall.setArgument(2, thisObj); + + resultArray->JSArray::put(exec, k, cachedCall.call()); + } + } + for (; k < length && !exec->hadException(); ++k) { + PropertySlot slot(thisObj); + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + JSValue v = slot.getValue(exec, k); + + MarkedArgumentBuffer eachArguments; + + eachArguments.append(v); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); + resultArray->put(exec, k, result); + } + + return JSValue::encode(resultArray); +} + +// Documentation for these three is available at: +// http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every +// http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach +// http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); + + JSValue result = jsBoolean(true); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { + JSFunction* f = asFunction(function); + JSArray* array = asArray(thisObj); + CachedCall cachedCall(exec, f, 3); + for (; k < length && !exec->hadException(); ++k) { + if (UNLIKELY(!array->canGetIndex(k))) + break; + + cachedCall.setThis(applyThis); + cachedCall.setArgument(0, array->getIndex(k)); + cachedCall.setArgument(1, jsNumber(k)); + cachedCall.setArgument(2, thisObj); + JSValue result = cachedCall.call(); + if (!result.toBoolean(cachedCall.newCallFrame(exec))) + return JSValue::encode(jsBoolean(false)); + } + } + for (; k < length && !exec->hadException(); ++k) { + PropertySlot slot(thisObj); + + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + MarkedArgumentBuffer eachArguments; + + eachArguments.append(slot.getValue(exec, k)); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); + + if (!predicateResult) { + result = jsBoolean(false); + break; + } + } + + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { + JSFunction* f = asFunction(function); + JSArray* array = asArray(thisObj); + CachedCall cachedCall(exec, f, 3); + for (; k < length && !exec->hadException(); ++k) { + if (UNLIKELY(!array->canGetIndex(k))) + break; + + cachedCall.setThis(applyThis); + cachedCall.setArgument(0, array->getIndex(k)); + cachedCall.setArgument(1, jsNumber(k)); + cachedCall.setArgument(2, thisObj); + + cachedCall.call(); + } + } + for (; k < length && !exec->hadException(); ++k) { + PropertySlot slot(thisObj); + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + MarkedArgumentBuffer eachArguments; + eachArguments.append(slot.getValue(exec, k)); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + call(exec, function, callType, callData, applyThis, eachArguments); + } + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); + + JSValue result = jsBoolean(false); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { + JSFunction* f = asFunction(function); + JSArray* array = asArray(thisObj); + CachedCall cachedCall(exec, f, 3); + for (; k < length && !exec->hadException(); ++k) { + if (UNLIKELY(!array->canGetIndex(k))) + break; + + cachedCall.setThis(applyThis); + cachedCall.setArgument(0, array->getIndex(k)); + cachedCall.setArgument(1, jsNumber(k)); + cachedCall.setArgument(2, thisObj); + JSValue result = cachedCall.call(); + if (result.toBoolean(cachedCall.newCallFrame(exec))) + return JSValue::encode(jsBoolean(true)); + } + } + for (; k < length && !exec->hadException(); ++k) { + PropertySlot slot(thisObj); + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + MarkedArgumentBuffer eachArguments; + eachArguments.append(slot.getValue(exec, k)); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); + + if (predicateResult) { + result = jsBoolean(true); + break; + } + } + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + unsigned i = 0; + JSValue rv; + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (!length && exec->argumentCount() == 1) + return throwVMTypeError(exec); + JSArray* array = 0; + if (isJSArray(&exec->globalData(), thisObj)) + array = asArray(thisObj); + + if (exec->argumentCount() >= 2) + rv = exec->argument(1); + else if (array && array->canGetIndex(0)){ + rv = array->getIndex(0); + i = 1; + } else { + for (i = 0; i < length; i++) { + rv = getProperty(exec, thisObj, i); + if (rv) + break; + } + if (!rv) + return throwVMTypeError(exec); + i++; + } + + if (callType == CallTypeJS && array) { + CachedCall cachedCall(exec, asFunction(function), 4); + for (; i < length && !exec->hadException(); ++i) { + cachedCall.setThis(jsNull()); + cachedCall.setArgument(0, rv); + JSValue v; + if (LIKELY(array->canGetIndex(i))) + v = array->getIndex(i); + else + break; // length has been made unsafe while we enumerate fallback to slow path + cachedCall.setArgument(1, v); + cachedCall.setArgument(2, jsNumber(i)); + cachedCall.setArgument(3, array); + rv = cachedCall.call(); + } + if (i == length) // only return if we reached the end of the array + return JSValue::encode(rv); + } + + for (; i < length && !exec->hadException(); ++i) { + JSValue prop = getProperty(exec, thisObj, i); + if (!prop) + continue; + + MarkedArgumentBuffer eachArguments; + eachArguments.append(rv); + eachArguments.append(prop); + eachArguments.append(jsNumber(i)); + eachArguments.append(thisObj); + + rv = call(exec, function, callType, callData, jsNull(), eachArguments); + } + return JSValue::encode(rv); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + unsigned i = 0; + JSValue rv; + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (!length && exec->argumentCount() == 1) + return throwVMTypeError(exec); + JSArray* array = 0; + if (isJSArray(&exec->globalData(), thisObj)) + array = asArray(thisObj); + + if (exec->argumentCount() >= 2) + rv = exec->argument(1); + else if (array && array->canGetIndex(length - 1)){ + rv = array->getIndex(length - 1); + i = 1; + } else { + for (i = 0; i < length; i++) { + rv = getProperty(exec, thisObj, length - i - 1); + if (rv) + break; + } + if (!rv) + return throwVMTypeError(exec); + i++; + } + + if (callType == CallTypeJS && array) { + CachedCall cachedCall(exec, asFunction(function), 4); + for (; i < length && !exec->hadException(); ++i) { + unsigned idx = length - i - 1; + cachedCall.setThis(jsNull()); + cachedCall.setArgument(0, rv); + if (UNLIKELY(!array->canGetIndex(idx))) + break; // length has been made unsafe while we enumerate fallback to slow path + cachedCall.setArgument(1, array->getIndex(idx)); + cachedCall.setArgument(2, jsNumber(idx)); + cachedCall.setArgument(3, array); + rv = cachedCall.call(); + } + if (i == length) // only return if we reached the end of the array + return JSValue::encode(rv); + } + + for (; i < length && !exec->hadException(); ++i) { + unsigned idx = length - i - 1; + JSValue prop = getProperty(exec, thisObj, idx); + if (!prop) + continue; + + MarkedArgumentBuffer eachArguments; + eachArguments.append(rv); + eachArguments.append(prop); + eachArguments.append(jsNumber(idx)); + eachArguments.append(thisObj); + + rv = call(exec, function, callType, callData, jsNull(), eachArguments); + } + return JSValue::encode(rv); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec) +{ + // JavaScript 1.5 Extension by Mozilla + // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); + + JSValue searchElement = exec->argument(0); + for (; index < length; ++index) { + JSValue e = getProperty(exec, thisObj, index); + if (!e) + continue; + if (JSValue::strictEqual(exec, searchElement, e)) + return JSValue::encode(jsNumber(index)); + } + + return JSValue::encode(jsNumber(-1)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec) +{ + // JavaScript 1.6 Extension by Mozilla + // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (!length) + return JSValue::encode(jsNumber(-1)); + + unsigned index = length - 1; + JSValue fromValue = exec->argument(1); + if (!fromValue.isUndefined()) { + double fromDouble = fromValue.toInteger(exec); + if (fromDouble < 0) { + fromDouble += length; + if (fromDouble < 0) + return JSValue::encode(jsNumber(-1)); + } + if (fromDouble < length) + index = static_cast<unsigned>(fromDouble); + } + + JSValue searchElement = exec->argument(0); + do { + ASSERT(index < length); + JSValue e = getProperty(exec, thisObj, index); + if (!e) + continue; + if (JSValue::strictEqual(exec, searchElement, e)) + return JSValue::encode(jsNumber(index)); + } while (index--); + + return JSValue::encode(jsNumber(-1)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.h b/Source/JavaScriptCore/runtime/ArrayPrototype.h new file mode 100644 index 0000000..42665e3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ArrayPrototype_h +#define ArrayPrototype_h + +#include "JSArray.h" +#include "Lookup.h" + +namespace JSC { + + class ArrayPrototype : public JSArray { + public: + explicit ArrayPrototype(JSGlobalObject*, 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; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned AnonymousSlotCount = JSArray::AnonymousSlotCount + 1; + }; + +} // namespace JSC + +#endif // ArrayPrototype_h diff --git a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h new file mode 100644 index 0000000..74089a5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -0,0 +1,55 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 BatchedTransitionOptimizer_h +#define BatchedTransitionOptimizer_h + +#include <wtf/Noncopyable.h> +#include "JSObject.h" + +namespace JSC { + + class BatchedTransitionOptimizer : public Noncopyable { + public: + BatchedTransitionOptimizer(JSObject* object) + : m_object(object) + { + if (!m_object->structure()->isDictionary()) + m_object->setStructure(Structure::toCacheableDictionaryTransition(m_object->structure())); + } + + ~BatchedTransitionOptimizer() + { + m_object->flattenDictionaryObject(); + } + + private: + JSObject* m_object; + }; + +} // namespace JSC + +#endif // BatchedTransitionOptimizer_h diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.cpp b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp new file mode 100644 index 0000000..0167e03 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "BooleanConstructor.h" + +#include "BooleanPrototype.h" +#include "JSGlobalObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(BooleanConstructor); + +BooleanConstructor::BooleanConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, BooleanPrototype* booleanPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, booleanPrototype->classInfo()->className)) +{ + putDirectWithoutTransition(exec->propertyNames().prototype, booleanPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +// ECMA 15.6.2 +JSObject* constructBoolean(ExecState* exec, const ArgList& args) +{ + BooleanObject* obj = new (exec) BooleanObject(exec->lexicalGlobalObject()->booleanObjectStructure()); + obj->setInternalValue(jsBoolean(args.at(0).toBoolean(exec))); + return obj; +} + +static EncodedJSValue JSC_HOST_CALL constructWithBooleanConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructBoolean(exec, args)); +} + +ConstructType BooleanConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithBooleanConstructor; + return ConstructTypeHost; +} + +// ECMA 15.6.1 +static EncodedJSValue JSC_HOST_CALL callBooleanConstructor(ExecState* exec) +{ + return JSValue::encode(jsBoolean(exec->argument(0).toBoolean(exec))); +} + +CallType BooleanConstructor::getCallData(CallData& callData) +{ + callData.native.function = callBooleanConstructor; + return CallTypeHost; +} + +JSObject* constructBooleanFromImmediateBoolean(ExecState* exec, JSValue immediateBooleanValue) +{ + BooleanObject* obj = new (exec) BooleanObject(exec->lexicalGlobalObject()->booleanObjectStructure()); + obj->setInternalValue(immediateBooleanValue); + return obj; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.h b/Source/JavaScriptCore/runtime/BooleanConstructor.h new file mode 100644 index 0000000..0f3efa7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BooleanConstructor_h +#define BooleanConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class BooleanPrototype; + + class BooleanConstructor : public InternalFunction { + public: + BooleanConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, BooleanPrototype*); + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + + JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSValue); + JSObject* constructBoolean(ExecState*, const ArgList&); + +} // namespace JSC + +#endif // BooleanConstructor_h diff --git a/Source/JavaScriptCore/runtime/BooleanObject.cpp b/Source/JavaScriptCore/runtime/BooleanObject.cpp new file mode 100644 index 0000000..c9b3846 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanObject.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "BooleanObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(BooleanObject); + +const ClassInfo BooleanObject::info = { "Boolean", 0, 0, 0 }; + +BooleanObject::BooleanObject(NonNullPassRefPtr<Structure> structure) + : JSWrapperObject(structure) +{ +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanObject.h b/Source/JavaScriptCore/runtime/BooleanObject.h new file mode 100644 index 0000000..4b02acb --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanObject.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BooleanObject_h +#define BooleanObject_h + +#include "JSWrapperObject.h" + +namespace JSC { + + class BooleanObject : public JSWrapperObject { + public: + 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, StructureFlags), AnonymousSlotCount); + } + }; + + BooleanObject* asBooleanObject(JSValue); + + inline BooleanObject* asBooleanObject(JSValue value) + { + ASSERT(asObject(value)->inherits(&BooleanObject::info)); + return static_cast<BooleanObject*>(asObject(value)); + } + +} // namespace JSC + +#endif // BooleanObject_h diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.cpp b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp new file mode 100644 index 0000000..7ffd095 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "BooleanPrototype.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSFunction.h" +#include "JSString.h" +#include "ObjectPrototype.h" +#include "PrototypeFunction.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(BooleanPrototype); + +// Functions +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState*); + +// ECMA 15.6.4 + +BooleanPrototype::BooleanPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) + : BooleanObject(structure) +{ + setInternalValue(jsBoolean(false)); + + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, booleanProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, booleanProtoFuncValueOf), DontEnum); +} + + +// ------------------------------ Functions -------------------------- + +// ECMA 15.6.4.2 + 15.6.4.3 + +EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue == jsBoolean(false)) + return JSValue::encode(jsNontrivialString(exec, "false")); + + if (thisValue == jsBoolean(true)) + return JSValue::encode(jsNontrivialString(exec, "true")); + + if (!thisValue.inherits(&BooleanObject::info)) + return throwVMTypeError(exec); + + if (asBooleanObject(thisValue)->internalValue() == jsBoolean(false)) + return JSValue::encode(jsNontrivialString(exec, "false")); + + ASSERT(asBooleanObject(thisValue)->internalValue() == jsBoolean(true)); + return JSValue::encode(jsNontrivialString(exec, "true")); +} + +EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isBoolean()) + return JSValue::encode(thisValue); + + if (!thisValue.inherits(&BooleanObject::info)) + return throwVMTypeError(exec); + + return JSValue::encode(asBooleanObject(thisValue)->internalValue()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.h b/Source/JavaScriptCore/runtime/BooleanPrototype.h new file mode 100644 index 0000000..ddadc43 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BooleanPrototype_h +#define BooleanPrototype_h + +#include "BooleanObject.h" + +namespace JSC { + + class BooleanPrototype : public BooleanObject { + public: + BooleanPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + }; + +} // namespace JSC + +#endif // BooleanPrototype_h diff --git a/Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h b/Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h new file mode 100644 index 0000000..67c7af8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 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 CachedTranscendentalFunction_h +#define CachedTranscendentalFunction_h + +#include "JSValue.h" + +namespace JSC { + +extern const double NaN; + +typedef double (*TranscendentalFunctionPtr)(double); + +// CachedTranscendentalFunction provides a generic mechanism to cache results +// for pure functions with the signature "double func(double)", and where NaN +// maps to NaN. +template<TranscendentalFunctionPtr orignalFunction> +class CachedTranscendentalFunction { + struct CacheEntry { + double operand; + double result; + }; + +public: + CachedTranscendentalFunction() + : m_cache(0) + { + } + + ~CachedTranscendentalFunction() + { + if (m_cache) + fastFree(m_cache); + } + + JSValue operator() (double operand) + { + if (UNLIKELY(!m_cache)) + initialize(); + CacheEntry* entry = &m_cache[hash(operand)]; + + if (entry->operand == operand) + return jsDoubleNumber(entry->result); + double result = orignalFunction(operand); + entry->operand = operand; + entry->result = result; + return jsDoubleNumber(result); + } + +private: + void initialize() + { + // Lazily allocate the table, populate with NaN->NaN mapping. + m_cache = static_cast<CacheEntry*>(fastMalloc(s_cacheSize * sizeof(CacheEntry))); + for (unsigned x = 0; x < s_cacheSize; ++x) { + m_cache[x].operand = NaN; + m_cache[x].result = NaN; + } + } + + static unsigned hash(double d) + { + union doubleAndUInt64 { + double d; + uint32_t is[2]; + } u; + u.d = d; + + unsigned x = u.is[0] ^ u.is[1]; + x = (x >> 20) ^ (x >> 8); + return x & (s_cacheSize - 1); + } + + static const unsigned s_cacheSize = 0x1000; + CacheEntry* m_cache; +}; + +} + +#endif // CachedTranscendentalFunction_h diff --git a/Source/JavaScriptCore/runtime/CallData.cpp b/Source/JavaScriptCore/runtime/CallData.cpp new file mode 100644 index 0000000..018e2ca --- /dev/null +++ b/Source/JavaScriptCore/runtime/CallData.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 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 "CallData.h" + +#include "Executable.h" +#include "Interpreter.h" +#include "JSFunction.h" + +namespace JSC { + +JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) +{ + ASSERT(callType == CallTypeJS || callType == CallTypeHost); + return exec->interpreter()->executeCall(exec, asObject(functionObject), callType, callData, thisValue, args); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CallData.h b/Source/JavaScriptCore/runtime/CallData.h new file mode 100644 index 0000000..32e1094 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CallData.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 CallData_h +#define CallData_h + +#include "JSValue.h" +#include "NativeFunctionWrapper.h" + +namespace JSC { + + class ArgList; + class ExecState; + class FunctionExecutable; + class JSObject; + class ScopeChainNode; + + enum CallType { + CallTypeNone, + CallTypeHost, + CallTypeJS + }; + + typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); + + union CallData { + struct { + NativeFunction function; + } native; + struct { + FunctionExecutable* functionExecutable; + ScopeChainNode* scopeChain; + } js; + }; + + JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); + +} // namespace JSC + +#endif // CallData_h diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h new file mode 100644 index 0000000..acec4e7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 + * 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. + * + */ + +#ifndef ClassInfo_h +#define ClassInfo_h + +#include "CallFrame.h" + +namespace JSC { + + class HashEntry; + struct HashTable; + + struct ClassInfo { + /** + * A string denoting the class name. Example: "Window". + */ + const char* className; + + /** + * Pointer to the class information of the base class. + * 0L if there is none. + */ + const ClassInfo* parentClass; + /** + * Static hash-table of properties. + * For classes that can be used from multiple threads, it is accessed via a getter function that would typically return a pointer to thread-specific value. + */ + const HashTable* propHashTable(ExecState* exec) const + { + if (classPropHashTableGetterFunction) + return classPropHashTableGetterFunction(exec); + return staticPropHashTable; + } + + const HashTable* staticPropHashTable; + typedef const HashTable* (*ClassPropHashTableGetterFunction)(ExecState*); + const ClassPropHashTableGetterFunction classPropHashTableGetterFunction; + }; + +} // namespace JSC + +#endif // ClassInfo_h diff --git a/Source/JavaScriptCore/runtime/Collector.cpp b/Source/JavaScriptCore/runtime/Collector.cpp new file mode 100644 index 0000000..38845ce --- /dev/null +++ b/Source/JavaScriptCore/runtime/Collector.cpp @@ -0,0 +1,1100 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "Collector.h" + +#include "ArgList.h" +#include "CallFrame.h" +#include "CodeBlock.h" +#include "CollectorHeapIterator.h" +#include "GCActivityCallback.h" +#include "Interpreter.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "JSONObject.h" +#include "JSString.h" +#include "JSValue.h" +#include "JSZombie.h" +#include "MarkStack.h" +#include "Nodes.h" +#include "Tracing.h" +#include <algorithm> +#include <limits.h> +#include <setjmp.h> +#include <stdlib.h> +#include <wtf/FastMalloc.h> +#include <wtf/HashCountedSet.h> +#include <wtf/WTFThreadData.h> +#include <wtf/UnusedParam.h> +#include <wtf/VMTags.h> + +#if OS(DARWIN) + +#include <mach/mach_init.h> +#include <mach/mach_port.h> +#include <mach/task.h> +#include <mach/thread_act.h> +#include <mach/vm_map.h> + +#elif OS(WINDOWS) + +#include <windows.h> +#include <malloc.h> + +#elif OS(HAIKU) + +#include <OS.h> + +#elif OS(UNIX) + +#include <stdlib.h> +#if !OS(HAIKU) +#include <sys/mman.h> +#endif +#include <unistd.h> + +#if OS(SOLARIS) +#include <thread.h> +#else +#include <pthread.h> +#endif + +#if HAVE(PTHREAD_NP_H) +#include <pthread_np.h> +#endif + +#if OS(QNX) +#include <fcntl.h> +#include <sys/procfs.h> +#include <stdio.h> +#include <errno.h> +#endif + +#endif + +#define COLLECT_ON_EVERY_ALLOCATION 0 + +using std::max; + +namespace JSC { + +// tunable parameters + +const size_t GROWTH_FACTOR = 2; +const size_t LOW_WATER_FACTOR = 4; +const size_t ALLOCATIONS_PER_COLLECTION = 3600; +// This value has to be a macro to be used in max() without introducing +// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. +#define MIN_ARRAY_SIZE (static_cast<size_t>(14)) + +#if ENABLE(JSC_MULTIPLE_THREADS) + +#if OS(DARWIN) +typedef mach_port_t PlatformThread; +#elif OS(WINDOWS) +typedef HANDLE PlatformThread; +#endif + +class Heap::Thread { +public: + Thread(pthread_t pthread, const PlatformThread& platThread, void* base) + : posixThread(pthread) + , platformThread(platThread) + , stackBase(base) + { + } + + Thread* next; + pthread_t posixThread; + PlatformThread platformThread; + void* stackBase; +}; + +#endif + +Heap::Heap(JSGlobalData* globalData) + : m_markListSet(0) +#if ENABLE(JSC_MULTIPLE_THREADS) + , m_registeredThreads(0) + , m_currentThreadRegistrar(0) +#endif + , m_globalData(globalData) +{ + ASSERT(globalData); + memset(&m_heap, 0, sizeof(CollectorHeap)); + allocateBlock(); + m_activityCallback = DefaultGCActivityCallback::create(this); + (*m_activityCallback)(); +} + +Heap::~Heap() +{ + // The destroy function must already have been called, so assert this. + ASSERT(!m_globalData); +} + +void Heap::destroy() +{ + JSLock lock(SilenceAssertionsOnly); + + if (!m_globalData) + return; + + ASSERT(!m_globalData->dynamicGlobalObject); + ASSERT(!isBusy()); + + // The global object is not GC protected at this point, so sweeping may delete it + // (and thus the global data) before other objects that may use the global data. + RefPtr<JSGlobalData> protect(m_globalData); + + delete m_markListSet; + m_markListSet = 0; + + freeBlocks(); + +#if ENABLE(JSC_MULTIPLE_THREADS) + if (m_currentThreadRegistrar) { + int error = pthread_key_delete(m_currentThreadRegistrar); + ASSERT_UNUSED(error, !error); + } + + MutexLocker registeredThreadsLock(m_registeredThreadsMutex); + for (Heap::Thread* t = m_registeredThreads; t;) { + Heap::Thread* next = t->next; + delete t; + t = next; + } +#endif + m_globalData = 0; +} + +NEVER_INLINE CollectorBlock* Heap::allocateBlock() +{ + PageAllocationAligned allocation = PageAllocationAligned::allocate(BLOCK_SIZE, BLOCK_SIZE, OSAllocator::JSGCHeapPages); + CollectorBlock* block = static_cast<CollectorBlock*>(allocation.base()); + if (!block) + CRASH(); + + // Initialize block. + + block->heap = this; + clearMarkBits(block); + + Structure* dummyMarkableCellStructure = m_globalData->dummyMarkableCellStructure.get(); + for (size_t i = 0; i < HeapConstants::cellsPerBlock; ++i) + new (&block->cells[i]) JSCell(dummyMarkableCellStructure); + + // Add block to blocks vector. + + size_t numBlocks = m_heap.numBlocks; + if (m_heap.usedBlocks == numBlocks) { + static const size_t maxNumBlocks = ULONG_MAX / sizeof(PageAllocationAligned) / GROWTH_FACTOR; + if (numBlocks > maxNumBlocks) + CRASH(); + numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); + m_heap.numBlocks = numBlocks; + m_heap.blocks = static_cast<PageAllocationAligned*>(fastRealloc(m_heap.blocks, numBlocks * sizeof(PageAllocationAligned))); + } + m_heap.blocks[m_heap.usedBlocks++] = allocation; + + return block; +} + +NEVER_INLINE void Heap::freeBlock(size_t block) +{ + m_heap.didShrink = true; + + ObjectIterator it(m_heap, block); + ObjectIterator end(m_heap, block + 1); + for ( ; it != end; ++it) + (*it)->~JSCell(); + m_heap.blocks[block].deallocate(); + + // swap with the last block so we compact as we go + m_heap.blocks[block] = m_heap.blocks[m_heap.usedBlocks - 1]; + m_heap.usedBlocks--; + + if (m_heap.numBlocks > MIN_ARRAY_SIZE && m_heap.usedBlocks < m_heap.numBlocks / LOW_WATER_FACTOR) { + m_heap.numBlocks = m_heap.numBlocks / GROWTH_FACTOR; + m_heap.blocks = static_cast<PageAllocationAligned*>(fastRealloc(m_heap.blocks, m_heap.numBlocks * sizeof(PageAllocationAligned))); + } +} + +void Heap::freeBlocks() +{ + ProtectCountSet protectedValuesCopy = m_protectedValues; + + clearMarkBits(); + ProtectCountSet::iterator protectedValuesEnd = protectedValuesCopy.end(); + for (ProtectCountSet::iterator it = protectedValuesCopy.begin(); it != protectedValuesEnd; ++it) + markCell(it->first); + + m_heap.nextCell = 0; + m_heap.nextBlock = 0; + DeadObjectIterator it(m_heap, m_heap.nextBlock, m_heap.nextCell); + DeadObjectIterator end(m_heap, m_heap.usedBlocks); + for ( ; it != end; ++it) + (*it)->~JSCell(); + + ASSERT(!protectedObjectCount()); + + protectedValuesEnd = protectedValuesCopy.end(); + for (ProtectCountSet::iterator it = protectedValuesCopy.begin(); it != protectedValuesEnd; ++it) + it->first->~JSCell(); + + for (size_t block = 0; block < m_heap.usedBlocks; ++block) + m_heap.blocks[block].deallocate(); + + fastFree(m_heap.blocks); + + memset(&m_heap, 0, sizeof(CollectorHeap)); +} + +void Heap::recordExtraCost(size_t cost) +{ + // Our frequency of garbage collection tries to balance memory use against speed + // by collecting based on the number of newly created values. However, for values + // that hold on to a great deal of memory that's not in the form of other JS values, + // that is not good enough - in some cases a lot of those objects can pile up and + // use crazy amounts of memory without a GC happening. So we track these extra + // memory costs. Only unusually large objects are noted, and we only keep track + // of this extra cost until the next GC. In garbage collected languages, most values + // are either very short lived temporaries, or have extremely long lifetimes. So + // if a large value survives one garbage collection, there is not much point to + // collecting more frequently as long as it stays alive. + + if (m_heap.extraCost > maxExtraCost && m_heap.extraCost > m_heap.usedBlocks * BLOCK_SIZE / 2) { + // If the last iteration through the heap deallocated blocks, we need + // to clean up remaining garbage before marking. Otherwise, the conservative + // marking mechanism might follow a pointer to unmapped memory. + if (m_heap.didShrink) + sweep(); + reset(); + } + m_heap.extraCost += cost; +} + +void* Heap::allocate(size_t s) +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + typedef HeapConstants::Block Block; + typedef HeapConstants::Cell Cell; + + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT_UNUSED(s, s <= HeapConstants::cellSize); + + ASSERT(m_heap.operationInProgress == NoOperation); + +#if COLLECT_ON_EVERY_ALLOCATION + collectAllGarbage(); + ASSERT(m_heap.operationInProgress == NoOperation); +#endif + +allocate: + + // Fast case: find the next garbage cell and recycle it. + + do { + ASSERT(m_heap.nextBlock < m_heap.usedBlocks); + Block* block = m_heap.collectorBlock(m_heap.nextBlock); + do { + ASSERT(m_heap.nextCell < HeapConstants::cellsPerBlock); + if (!block->marked.get(m_heap.nextCell)) { // Always false for the last cell in the block + Cell* cell = &block->cells[m_heap.nextCell]; + + m_heap.operationInProgress = Allocation; + JSCell* imp = reinterpret_cast<JSCell*>(cell); + imp->~JSCell(); + m_heap.operationInProgress = NoOperation; + + ++m_heap.nextCell; + return cell; + } + block->marked.advanceToNextPossibleFreeCell(m_heap.nextCell); + } while (m_heap.nextCell != HeapConstants::cellsPerBlock); + m_heap.nextCell = 0; + } while (++m_heap.nextBlock != m_heap.usedBlocks); + + // Slow case: reached the end of the heap. Mark live objects and start over. + + reset(); + goto allocate; +} + +void Heap::resizeBlocks() +{ + m_heap.didShrink = false; + + size_t usedCellCount = markedCells(); + size_t minCellCount = usedCellCount + max(ALLOCATIONS_PER_COLLECTION, usedCellCount); + size_t minBlockCount = (minCellCount + HeapConstants::cellsPerBlock - 1) / HeapConstants::cellsPerBlock; + + size_t maxCellCount = 1.25f * minCellCount; + size_t maxBlockCount = (maxCellCount + HeapConstants::cellsPerBlock - 1) / HeapConstants::cellsPerBlock; + + if (m_heap.usedBlocks < minBlockCount) + growBlocks(minBlockCount); + else if (m_heap.usedBlocks > maxBlockCount) + shrinkBlocks(maxBlockCount); +} + +void Heap::growBlocks(size_t neededBlocks) +{ + ASSERT(m_heap.usedBlocks < neededBlocks); + while (m_heap.usedBlocks < neededBlocks) + allocateBlock(); +} + +void Heap::shrinkBlocks(size_t neededBlocks) +{ + ASSERT(m_heap.usedBlocks > neededBlocks); + + // Clear the always-on last bit, so isEmpty() isn't fooled by it. + for (size_t i = 0; i < m_heap.usedBlocks; ++i) + m_heap.collectorBlock(i)->marked.clear(HeapConstants::cellsPerBlock - 1); + + for (size_t i = 0; i != m_heap.usedBlocks && m_heap.usedBlocks != neededBlocks; ) { + if (m_heap.collectorBlock(i)->marked.isEmpty()) { + freeBlock(i); + } else + ++i; + } + + // Reset the always-on last bit. + for (size_t i = 0; i < m_heap.usedBlocks; ++i) + m_heap.collectorBlock(i)->marked.set(HeapConstants::cellsPerBlock - 1); +} + +#if ENABLE(JSC_MULTIPLE_THREADS) + +static inline PlatformThread getCurrentPlatformThread() +{ +#if OS(DARWIN) + return pthread_mach_thread_np(pthread_self()); +#elif OS(WINDOWS) + return pthread_getw32threadhandle_np(pthread_self()); +#endif +} + +void Heap::makeUsableFromMultipleThreads() +{ + if (m_currentThreadRegistrar) + return; + + int error = pthread_key_create(&m_currentThreadRegistrar, unregisterThread); + if (error) + CRASH(); +} + +void Heap::registerThread() +{ + ASSERT(!m_globalData->exclusiveThread || m_globalData->exclusiveThread == currentThread()); + + if (!m_currentThreadRegistrar || pthread_getspecific(m_currentThreadRegistrar)) + return; + + pthread_setspecific(m_currentThreadRegistrar, this); + Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), m_globalData->stack().origin()); + + MutexLocker lock(m_registeredThreadsMutex); + + thread->next = m_registeredThreads; + m_registeredThreads = thread; +} + +void Heap::unregisterThread(void* p) +{ + if (p) + static_cast<Heap*>(p)->unregisterThread(); +} + +void Heap::unregisterThread() +{ + pthread_t currentPosixThread = pthread_self(); + + MutexLocker lock(m_registeredThreadsMutex); + + if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) { + Thread* t = m_registeredThreads; + m_registeredThreads = m_registeredThreads->next; + delete t; + } else { + Heap::Thread* last = m_registeredThreads; + Heap::Thread* t; + for (t = m_registeredThreads->next; t; t = t->next) { + if (pthread_equal(t->posixThread, currentPosixThread)) { + last->next = t->next; + break; + } + last = t; + } + ASSERT(t); // If t is NULL, we never found ourselves in the list. + delete t; + } +} + +#else // ENABLE(JSC_MULTIPLE_THREADS) + +void Heap::registerThread() +{ +} + +#endif + +inline bool isPointerAligned(void* p) +{ + return (((intptr_t)(p) & (sizeof(char*) - 1)) == 0); +} + +// Cell size needs to be a power of two for isPossibleCell to be valid. +COMPILE_ASSERT(sizeof(CollectorCell) % 2 == 0, Collector_cell_size_is_power_of_two); + +static inline bool isCellAligned(void *p) +{ + return (((intptr_t)(p) & CELL_MASK) == 0); +} + +static inline bool isPossibleCell(void* p) +{ + return isCellAligned(p) && p; +} + +void Heap::markConservatively(MarkStack& markStack, void* start, void* end) +{ +#if OS(WINCE) + if (start > end) { + void* tmp = start; + start = end; + end = tmp; + } +#else + ASSERT(start <= end); +#endif + + ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000); + ASSERT(isPointerAligned(start)); + ASSERT(isPointerAligned(end)); + + char** p = static_cast<char**>(start); + char** e = static_cast<char**>(end); + + while (p != e) { + char* x = *p++; + if (isPossibleCell(x)) { + size_t usedBlocks; + uintptr_t xAsBits = reinterpret_cast<uintptr_t>(x); + xAsBits &= CELL_ALIGN_MASK; + + uintptr_t offset = xAsBits & BLOCK_OFFSET_MASK; + const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1); + if (offset > lastCellOffset) + continue; + + CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset); + usedBlocks = m_heap.usedBlocks; + for (size_t block = 0; block < usedBlocks; block++) { + if (m_heap.collectorBlock(block) != blockAddr) + continue; + markStack.append(reinterpret_cast<JSCell*>(xAsBits)); + } + } + } +} + +void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markStack) +{ + markConservatively(markStack, m_globalData->stack().current(), m_globalData->stack().origin()); + markStack.drain(); +} + +#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 REGISTER_BUFFER_ALIGNMENT; +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4611) +#endif + setjmp(registers); +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + + markCurrentThreadConservativelyInternal(markStack); +} + +#if ENABLE(JSC_MULTIPLE_THREADS) + +static inline void suspendThread(const PlatformThread& platformThread) +{ +#if OS(DARWIN) + thread_suspend(platformThread); +#elif OS(WINDOWS) + SuspendThread(platformThread); +#else +#error Need a way to suspend threads on this platform +#endif +} + +static inline void resumeThread(const PlatformThread& platformThread) +{ +#if OS(DARWIN) + thread_resume(platformThread); +#elif OS(WINDOWS) + ResumeThread(platformThread); +#else +#error Need a way to resume threads on this platform +#endif +} + +typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit + +#if OS(DARWIN) + +#if CPU(X86) +typedef i386_thread_state_t PlatformThreadRegisters; +#elif CPU(X86_64) +typedef x86_thread_state64_t PlatformThreadRegisters; +#elif CPU(PPC) +typedef ppc_thread_state_t PlatformThreadRegisters; +#elif CPU(PPC64) +typedef ppc_thread_state64_t PlatformThreadRegisters; +#elif CPU(ARM) +typedef arm_thread_state_t PlatformThreadRegisters; +#else +#error Unknown Architecture +#endif + +#elif OS(WINDOWS) && CPU(X86) +typedef CONTEXT PlatformThreadRegisters; +#else +#error Need a thread register struct for this platform +#endif + +static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs) +{ +#if OS(DARWIN) + +#if CPU(X86) + unsigned user_count = sizeof(regs)/sizeof(int); + thread_state_flavor_t flavor = i386_THREAD_STATE; +#elif CPU(X86_64) + unsigned user_count = x86_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = x86_THREAD_STATE64; +#elif CPU(PPC) + unsigned user_count = PPC_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE; +#elif CPU(PPC64) + unsigned user_count = PPC_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE64; +#elif CPU(ARM) + unsigned user_count = ARM_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = ARM_THREAD_STATE; +#else +#error Unknown Architecture +#endif + + kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); + if (result != KERN_SUCCESS) { + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, + "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result); + CRASH(); + } + return user_count * sizeof(usword_t); +// end OS(DARWIN) + +#elif OS(WINDOWS) && CPU(X86) + regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; + GetThreadContext(platformThread, ®s); + return sizeof(CONTEXT); +#else +#error Need a way to get thread registers on this platform +#endif +} + +static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) +{ +#if OS(DARWIN) + +#if __DARWIN_UNIX03 + +#if CPU(X86) + return reinterpret_cast<void*>(regs.__esp); +#elif CPU(X86_64) + return reinterpret_cast<void*>(regs.__rsp); +#elif CPU(PPC) || CPU(PPC64) + return reinterpret_cast<void*>(regs.__r1); +#elif CPU(ARM) + return reinterpret_cast<void*>(regs.__sp); +#else +#error Unknown Architecture +#endif + +#else // !__DARWIN_UNIX03 + +#if CPU(X86) + return reinterpret_cast<void*>(regs.esp); +#elif CPU(X86_64) + return reinterpret_cast<void*>(regs.rsp); +#elif CPU(PPC) || CPU(PPC64) + return reinterpret_cast<void*>(regs.r1); +#else +#error Unknown Architecture +#endif + +#endif // __DARWIN_UNIX03 + +// end OS(DARWIN) +#elif CPU(X86) && OS(WINDOWS) + return reinterpret_cast<void*>((uintptr_t) regs.Esp); +#else +#error Need a way to get the stack pointer for another thread on this platform +#endif +} + +void Heap::markOtherThreadConservatively(MarkStack& markStack, Thread* thread) +{ + suspendThread(thread->platformThread); + + PlatformThreadRegisters regs; + size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); + + // mark the thread's registers + markConservatively(markStack, static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize)); + markStack.drain(); + + void* stackPointer = otherThreadStackPointer(regs); + markConservatively(markStack, stackPointer, thread->stackBase); + markStack.drain(); + + resumeThread(thread->platformThread); +} + +#endif + +void Heap::markStackObjectsConservatively(MarkStack& markStack) +{ + markCurrentThreadConservatively(markStack); + +#if ENABLE(JSC_MULTIPLE_THREADS) + + if (m_currentThreadRegistrar) { + + MutexLocker lock(m_registeredThreadsMutex); + +#ifndef NDEBUG + // Forbid malloc during the mark phase. Marking a thread suspends it, so + // a malloc inside markChildren() would risk a deadlock with a thread that had been + // suspended while holding the malloc lock. + fastMallocForbid(); +#endif + // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held, + // and since this is a shared heap, they are real locks. + for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { + if (!pthread_equal(thread->posixThread, pthread_self())) + markOtherThreadConservatively(markStack, thread); + } +#ifndef NDEBUG + fastMallocAllow(); +#endif + } +#endif +} + +void Heap::updateWeakGCHandles() +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + weakGCHandlePool(i)->update(); +} + +void WeakGCHandlePool::update() +{ + for (unsigned i = 1; i < WeakGCHandlePool::numPoolEntries; ++i) { + if (m_entries[i].isValidPtr()) { + JSCell* cell = m_entries[i].get(); + if (!cell || !Heap::isCellMarked(cell)) + m_entries[i].invalidate(); + } + } +} + +WeakGCHandle* Heap::addWeakGCHandle(JSCell* ptr) +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + if (!weakGCHandlePool(i)->isFull()) + return weakGCHandlePool(i)->allocate(ptr); + + PageAllocationAligned allocation = PageAllocationAligned::allocate(WeakGCHandlePool::poolSize, WeakGCHandlePool::poolSize, OSAllocator::JSGCHeapPages); + m_weakGCHandlePools.append(allocation); + + WeakGCHandlePool* pool = new (allocation.base()) WeakGCHandlePool(); + return pool->allocate(ptr); +} + +void Heap::protect(JSValue k) +{ + ASSERT(k); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); + + if (!k.isCell()) + return; + + m_protectedValues.add(k.asCell()); +} + +bool Heap::unprotect(JSValue k) +{ + ASSERT(k); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); + + if (!k.isCell()) + return false; + + return m_protectedValues.remove(k.asCell()); +} + +void Heap::markProtectedObjects(MarkStack& markStack) +{ + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { + markStack.append(it->first); + markStack.drain(); + } +} + +void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector) +{ + m_tempSortingVectors.append(tempVector); +} + +void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector) +{ + ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last()); + m_tempSortingVectors.removeLast(); +} + +void Heap::markTempSortVectors(MarkStack& markStack) +{ + typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors; + + VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end(); + for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) { + Vector<ValueStringPair>* tempSortingVector = *it; + + Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end(); + for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) + if (vectorIt->first) + markStack.append(vectorIt->first); + markStack.drain(); + } +} + +void Heap::clearMarkBits() +{ + for (size_t i = 0; i < m_heap.usedBlocks; ++i) + clearMarkBits(m_heap.collectorBlock(i)); +} + +void Heap::clearMarkBits(CollectorBlock* block) +{ + // allocate assumes that the last cell in every block is marked. + block->marked.clearAll(); + block->marked.set(HeapConstants::cellsPerBlock - 1); +} + +size_t Heap::markedCells(size_t startBlock, size_t startCell) const +{ + ASSERT(startBlock <= m_heap.usedBlocks); + ASSERT(startCell < HeapConstants::cellsPerBlock); + + if (startBlock >= m_heap.usedBlocks) + return 0; + + size_t result = 0; + result += m_heap.collectorBlock(startBlock)->marked.count(startCell); + for (size_t i = startBlock + 1; i < m_heap.usedBlocks; ++i) + result += m_heap.collectorBlock(i)->marked.count(); + + return result; +} + +void Heap::sweep() +{ + ASSERT(m_heap.operationInProgress == NoOperation); + if (m_heap.operationInProgress != NoOperation) + CRASH(); + m_heap.operationInProgress = Collection; + +#if !ENABLE(JSC_ZOMBIES) + Structure* dummyMarkableCellStructure = m_globalData->dummyMarkableCellStructure.get(); +#endif + + DeadObjectIterator it(m_heap, m_heap.nextBlock, m_heap.nextCell); + DeadObjectIterator end(m_heap, m_heap.usedBlocks); + for ( ; it != end; ++it) { + JSCell* cell = *it; +#if ENABLE(JSC_ZOMBIES) + if (!cell->isZombie()) { + const ClassInfo* info = cell->classInfo(); + cell->~JSCell(); + new (cell) JSZombie(info, JSZombie::leakedZombieStructure()); + Heap::markCell(cell); + } +#else + cell->~JSCell(); + // Callers of sweep assume it's safe to mark any cell in the heap. + new (cell) JSCell(dummyMarkableCellStructure); +#endif + } + + m_heap.operationInProgress = NoOperation; +} + +void Heap::markRoots() +{ +#ifndef NDEBUG + if (m_globalData->isSharedInstance()) { + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + } +#endif + + ASSERT(m_heap.operationInProgress == NoOperation); + if (m_heap.operationInProgress != NoOperation) + CRASH(); + + m_heap.operationInProgress = Collection; + + MarkStack& markStack = m_globalData->markStack; + + // Reset mark bits. + clearMarkBits(); + + // Mark stack roots. + markStackObjectsConservatively(markStack); + m_globalData->interpreter->registerFile().markCallFrames(markStack, this); + + // Mark explicitly registered roots. + markProtectedObjects(markStack); + + // Mark temporary vector for Array sorting + markTempSortVectors(markStack); + + // Mark misc. other roots. + if (m_markListSet && m_markListSet->size()) + MarkedArgumentBuffer::markLists(markStack, *m_markListSet); + if (m_globalData->exception) + markStack.append(m_globalData->exception); + if (m_globalData->firstStringifierToMark) + JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); + + // Mark the small strings cache last, since it will clear itself if nothing + // else has marked it. + m_globalData->smallStrings.markChildren(markStack); + + markStack.drain(); + markStack.compact(); + + updateWeakGCHandles(); + + m_heap.operationInProgress = NoOperation; +} + +size_t Heap::objectCount() const +{ + return m_heap.nextBlock * HeapConstants::cellsPerBlock // allocated full blocks + + m_heap.nextCell // allocated cells in current block + + markedCells(m_heap.nextBlock, m_heap.nextCell) // marked cells in remainder of m_heap + - m_heap.usedBlocks; // 1 cell per block is a dummy sentinel +} + +void Heap::addToStatistics(Heap::Statistics& statistics) const +{ + statistics.size += m_heap.usedBlocks * BLOCK_SIZE; + statistics.free += m_heap.usedBlocks * BLOCK_SIZE - (objectCount() * HeapConstants::cellSize); +} + +Heap::Statistics Heap::statistics() const +{ + Statistics statistics = { 0, 0 }; + addToStatistics(statistics); + return statistics; +} + +size_t Heap::size() const +{ + return m_heap.usedBlocks * BLOCK_SIZE; +} + +size_t Heap::globalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + ++count; + o = o->next(); + } while (o != head); + } + return count; +} + +size_t Heap::protectedGlobalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + if (m_protectedValues.contains(o)) + ++count; + o = o->next(); + } while (o != head); + } + + return count; +} + +size_t Heap::protectedObjectCount() +{ + return m_protectedValues.size(); +} + +static const char* typeName(JSCell* cell) +{ + if (cell->isString()) + return "string"; + if (cell->isGetterSetter()) + return "Getter-Setter"; + if (cell->isAPIValueWrapper()) + return "API wrapper"; + if (cell->isPropertyNameIterator()) + return "For-in iterator"; + if (!cell->isObject()) + return "[empty cell]"; + const ClassInfo* info = cell->classInfo(); + return info ? info->className : "Object"; +} + +HashCountedSet<const char*>* Heap::protectedObjectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) + counts->add(typeName(it->first)); + + return counts; +} + +HashCountedSet<const char*>* Heap::objectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + LiveObjectIterator it = primaryHeapBegin(); + LiveObjectIterator heapEnd = primaryHeapEnd(); + for ( ; it != heapEnd; ++it) + counts->add(typeName(*it)); + + return counts; +} + +bool Heap::isBusy() +{ + return m_heap.operationInProgress != NoOperation; +} + +void Heap::reset() +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + JAVASCRIPTCORE_GC_BEGIN(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_heap.nextCell = 0; + m_heap.nextBlock = 0; + m_heap.nextNumber = 0; + m_heap.extraCost = 0; +#if ENABLE(JSC_ZOMBIES) + sweep(); +#endif + resizeBlocks(); + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); +} + +void Heap::collectAllGarbage() +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + JAVASCRIPTCORE_GC_BEGIN(); + + // If the last iteration through the heap deallocated blocks, we need + // to clean up remaining garbage before marking. Otherwise, the conservative + // marking mechanism might follow a pointer to unmapped memory. + if (m_heap.didShrink) + sweep(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_heap.nextCell = 0; + m_heap.nextBlock = 0; + m_heap.nextNumber = 0; + m_heap.extraCost = 0; + sweep(); + resizeBlocks(); + + JAVASCRIPTCORE_GC_END(); +} + +LiveObjectIterator Heap::primaryHeapBegin() +{ + return LiveObjectIterator(m_heap, 0); +} + +LiveObjectIterator Heap::primaryHeapEnd() +{ + return LiveObjectIterator(m_heap, m_heap.usedBlocks); +} + +void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback) +{ + m_activityCallback = activityCallback; +} + +GCActivityCallback* Heap::activityCallback() +{ + return m_activityCallback.get(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Collector.h b/Source/JavaScriptCore/runtime/Collector.h new file mode 100644 index 0000000..a4e2fe1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Collector.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef Collector_h +#define Collector_h + +#include "GCHandle.h" +#include "JSValue.h" +#include <stddef.h> +#include <string.h> +#include <wtf/Bitmap.h> +#include <wtf/FixedArray.h> +#include <wtf/HashCountedSet.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PageAllocation.h> +#include <wtf/PageAllocationAligned.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Threading.h> + +#if ENABLE(JSC_MULTIPLE_THREADS) +#include <pthread.h> +#endif + +#define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) + +namespace JSC { + + class CollectorBlock; + class GCActivityCallback; + class JSCell; + class JSGlobalData; + class JSValue; + class MarkedArgumentBuffer; + class MarkStack; + + enum OperationInProgress { NoOperation, Allocation, Collection }; + + class LiveObjectIterator; + +#if OS(WINCE) || OS(SYMBIAN) || PLATFORM(BREWMP) + const size_t BLOCK_SIZE = 64 * 1024; // 64k +#else + const size_t BLOCK_SIZE = 256 * 1024; // 256k +#endif + + struct CollectorHeap { + size_t nextBlock; + size_t nextCell; + PageAllocationAligned* blocks; + + void* nextNumber; + + size_t numBlocks; + size_t usedBlocks; + + size_t extraCost; + bool didShrink; + + OperationInProgress operationInProgress; + + CollectorBlock* collectorBlock(size_t index) const + { + return static_cast<CollectorBlock*>(blocks[index].base()); + } + }; + + class Heap : public Noncopyable { + public: + class Thread; + + void destroy(); + + void* allocateNumber(size_t); + void* allocate(size_t); + + bool isBusy(); // true if an allocation or collection is in progress + void collectAllGarbage(); + + GCActivityCallback* activityCallback(); + void setActivityCallback(PassOwnPtr<GCActivityCallback>); + + static const size_t minExtraCost = 256; + static const size_t maxExtraCost = 1024 * 1024; + + void reportExtraMemoryCost(size_t cost); + + size_t objectCount() const; + struct Statistics { + size_t size; + size_t free; + }; + Statistics statistics() const; + size_t size() const; + + void protect(JSValue); + // Returns true if the value is no longer protected by any protect pointers + // (though it may still be alive due to heap/stack references). + bool unprotect(JSValue); + + static Heap* heap(JSValue); // 0 for immediate values + static Heap* heap(JSCell*); + + size_t globalObjectCount(); + size_t protectedObjectCount(); + size_t protectedGlobalObjectCount(); + HashCountedSet<const char*>* protectedObjectTypeCounts(); + HashCountedSet<const char*>* objectTypeCounts(); + + void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. + + static bool isCellMarked(const JSCell*); + static bool checkMarkCell(const JSCell*); + static void markCell(JSCell*); + + WeakGCHandle* addWeakGCHandle(JSCell*); + + void markConservatively(MarkStack&, void* start, void* end); + + void pushTempSortVector(WTF::Vector<ValueStringPair>*); + void popTempSortVector(WTF::Vector<ValueStringPair>*); + + HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } + + JSGlobalData* globalData() const { return m_globalData; } + static bool isNumber(JSCell*); + + LiveObjectIterator primaryHeapBegin(); + LiveObjectIterator primaryHeapEnd(); + + private: + void reset(); + void sweep(); + static CollectorBlock* cellBlock(const JSCell*); + static size_t cellOffset(const JSCell*); + + friend class JSGlobalData; + Heap(JSGlobalData*); + ~Heap(); + + NEVER_INLINE CollectorBlock* allocateBlock(); + NEVER_INLINE void freeBlock(size_t); + void freeBlocks(); + void resizeBlocks(); + void growBlocks(size_t neededBlocks); + void shrinkBlocks(size_t neededBlocks); + void clearMarkBits(); + void clearMarkBits(CollectorBlock*); + size_t markedCells(size_t startBlock = 0, size_t startCell = 0) const; + + void recordExtraCost(size_t); + + void addToStatistics(Statistics&) const; + + void markRoots(); + void markProtectedObjects(MarkStack&); + void markTempSortVectors(MarkStack&); + void markCurrentThreadConservatively(MarkStack&); + void markCurrentThreadConservativelyInternal(MarkStack&); + void markOtherThreadConservatively(MarkStack&, Thread*); + void markStackObjectsConservatively(MarkStack&); + + void updateWeakGCHandles(); + WeakGCHandlePool* weakGCHandlePool(size_t index); + + typedef HashCountedSet<JSCell*> ProtectCountSet; + + CollectorHeap m_heap; + + ProtectCountSet m_protectedValues; + WTF::Vector<PageAllocationAligned> m_weakGCHandlePools; + WTF::Vector<WTF::Vector<ValueStringPair>* > m_tempSortingVectors; + + HashSet<MarkedArgumentBuffer*>* m_markListSet; + + OwnPtr<GCActivityCallback> m_activityCallback; + +#if ENABLE(JSC_MULTIPLE_THREADS) + void makeUsableFromMultipleThreads(); + + static void unregisterThread(void*); + void unregisterThread(); + + Mutex m_registeredThreadsMutex; + Thread* m_registeredThreads; + pthread_key_t m_currentThreadRegistrar; +#endif + + JSGlobalData* m_globalData; + }; + + // tunable parameters + // derived constants + const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; + const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK; + const size_t MINIMUM_CELL_SIZE = 64; + const size_t CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0); + const size_t CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double); + const size_t SMALL_CELL_SIZE = CELL_SIZE / 2; + const size_t CELL_MASK = CELL_SIZE - 1; + const size_t CELL_ALIGN_MASK = ~CELL_MASK; + const size_t CELLS_PER_BLOCK = (BLOCK_SIZE - sizeof(Heap*)) * 8 * CELL_SIZE / (8 * CELL_SIZE + 1) / CELL_SIZE; // one bitmap byte can represent 8 cells. + + const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8; + const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); + + struct CollectorBitmap { + FixedArray<uint32_t, BITMAP_WORDS> bits; + bool get(size_t n) const { return !!(bits[n >> 5] & (1 << (n & 0x1F))); } + void set(size_t n) { bits[n >> 5] |= (1 << (n & 0x1F)); } + bool getset(size_t n) + { + unsigned i = (1 << (n & 0x1F)); + uint32_t& b = bits[n >> 5]; + bool r = !!(b & i); + b |= i; + return r; + } + void clear(size_t n) { bits[n >> 5] &= ~(1 << (n & 0x1F)); } + void clearAll() { memset(bits.data(), 0, sizeof(bits)); } + ALWAYS_INLINE void advanceToNextPossibleFreeCell(size_t& startCell) + { + if (!~bits[startCell >> 5]) + startCell = (startCell & (~0x1F)) + 32; + else + ++startCell; + } + size_t count(size_t startCell = 0) + { + size_t result = 0; + for ( ; (startCell & 0x1F) != 0; ++startCell) { + if (get(startCell)) + ++result; + } + for (size_t i = startCell >> 5; i < BITMAP_WORDS; ++i) + result += WTF::bitCount(bits[i]); + return result; + } + size_t isEmpty() // Much more efficient than testing count() == 0. + { + for (size_t i = 0; i < BITMAP_WORDS; ++i) + if (bits[i] != 0) + return false; + return true; + } + }; + + struct CollectorCell { + FixedArray<double, CELL_ARRAY_LENGTH> memory; + }; + + class CollectorBlock { + public: + FixedArray<CollectorCell, CELLS_PER_BLOCK> cells; + CollectorBitmap marked; + Heap* heap; + }; + + struct HeapConstants { + static const size_t cellSize = CELL_SIZE; + static const size_t cellsPerBlock = CELLS_PER_BLOCK; + typedef CollectorCell Cell; + typedef CollectorBlock Block; + }; + + inline CollectorBlock* Heap::cellBlock(const JSCell* cell) + { + return reinterpret_cast<CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); + } + + inline size_t Heap::cellOffset(const JSCell* cell) + { + return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; + } + + inline bool Heap::isCellMarked(const JSCell* cell) + { + return cellBlock(cell)->marked.get(cellOffset(cell)); + } + + inline bool Heap::checkMarkCell(const JSCell* cell) + { + return cellBlock(cell)->marked.getset(cellOffset(cell)); + } + + inline void Heap::markCell(JSCell* cell) + { + cellBlock(cell)->marked.set(cellOffset(cell)); + } + + inline void Heap::reportExtraMemoryCost(size_t cost) + { + if (cost > minExtraCost) + recordExtraCost(cost); + } + + inline void* Heap::allocateNumber(size_t s) + { + if (void* result = m_heap.nextNumber) { + m_heap.nextNumber = 0; + return result; + } + + void* result = allocate(s); + m_heap.nextNumber = static_cast<char*>(result) + (CELL_SIZE / 2); + return result; + } + + + inline WeakGCHandlePool* Heap::weakGCHandlePool(size_t index) + { + return static_cast<WeakGCHandlePool*>(m_weakGCHandlePools[index].base()); + } +} // namespace JSC + +#endif /* Collector_h */ diff --git a/Source/JavaScriptCore/runtime/CollectorHeapIterator.h b/Source/JavaScriptCore/runtime/CollectorHeapIterator.h new file mode 100644 index 0000000..9d107b7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CollectorHeapIterator.h @@ -0,0 +1,140 @@ +/* + * 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 + * 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 "Collector.h" + +#ifndef CollectorHeapIterator_h +#define CollectorHeapIterator_h + +namespace JSC { + + class CollectorHeapIterator { + public: + bool operator!=(const CollectorHeapIterator& other); + JSCell* operator*() const; + + protected: + CollectorHeapIterator(CollectorHeap&, size_t startBlock, size_t startCell); + void advance(size_t max); + + CollectorHeap& m_heap; + size_t m_block; + size_t m_cell; + }; + + class LiveObjectIterator : public CollectorHeapIterator { + public: + LiveObjectIterator(CollectorHeap&, size_t startBlock, size_t startCell = 0); + LiveObjectIterator& operator++(); + }; + + class DeadObjectIterator : public CollectorHeapIterator { + public: + DeadObjectIterator(CollectorHeap&, size_t startBlock, size_t startCell = 0); + DeadObjectIterator& operator++(); + }; + + class ObjectIterator : public CollectorHeapIterator { + public: + ObjectIterator(CollectorHeap&, size_t startBlock, size_t startCell = 0); + ObjectIterator& operator++(); + }; + + inline CollectorHeapIterator::CollectorHeapIterator(CollectorHeap& heap, size_t startBlock, size_t startCell) + : m_heap(heap) + , m_block(startBlock) + , m_cell(startCell) + { + } + + inline bool CollectorHeapIterator::operator!=(const CollectorHeapIterator& other) + { + return m_block != other.m_block || m_cell != other.m_cell; + } + + inline JSCell* CollectorHeapIterator::operator*() const + { + return reinterpret_cast<JSCell*>(&m_heap.collectorBlock(m_block)->cells[m_cell]); + } + + // Iterators advance up to the next-to-last -- and not the last -- cell in a + // block, since the last cell is a dummy sentinel. + inline void CollectorHeapIterator::advance(size_t max) + { + ++m_cell; + if (m_cell == max) { + m_cell = 0; + ++m_block; + } + } + + inline LiveObjectIterator::LiveObjectIterator(CollectorHeap& heap, size_t startBlock, size_t startCell) + : CollectorHeapIterator(heap, startBlock, startCell - 1) + { + ++(*this); + } + + inline LiveObjectIterator& LiveObjectIterator::operator++() + { + advance(HeapConstants::cellsPerBlock - 1); + if (m_block < m_heap.nextBlock || (m_block == m_heap.nextBlock && m_cell < m_heap.nextCell)) + return *this; + + while (m_block < m_heap.usedBlocks && !m_heap.collectorBlock(m_block)->marked.get(m_cell)) + advance(HeapConstants::cellsPerBlock - 1); + return *this; + } + + inline DeadObjectIterator::DeadObjectIterator(CollectorHeap& heap, size_t startBlock, size_t startCell) + : CollectorHeapIterator(heap, startBlock, startCell - 1) + { + ++(*this); + } + + inline DeadObjectIterator& DeadObjectIterator::operator++() + { + do { + advance(HeapConstants::cellsPerBlock - 1); + ASSERT(m_block > m_heap.nextBlock || (m_block == m_heap.nextBlock && m_cell >= m_heap.nextCell)); + } while (m_block < m_heap.usedBlocks && m_heap.collectorBlock(m_block)->marked.get(m_cell)); + return *this; + } + + inline ObjectIterator::ObjectIterator(CollectorHeap& heap, size_t startBlock, size_t startCell) + : CollectorHeapIterator(heap, startBlock, startCell - 1) + { + ++(*this); + } + + inline ObjectIterator& ObjectIterator::operator++() + { + advance(HeapConstants::cellsPerBlock - 1); + return *this; + } + +} // namespace JSC + +#endif // CollectorHeapIterator_h diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp new file mode 100644 index 0000000..1561102 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003, 2007, 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 + * 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 "CommonIdentifiers.h" + +namespace JSC { + +static const char* const nullCString = 0; + +#define INITIALIZE_PROPERTY_NAME(name) , name(globalData, #name) + +CommonIdentifiers::CommonIdentifiers(JSGlobalData* globalData) + : nullIdentifier(globalData, nullCString) + , emptyIdentifier(globalData, "") + , underscoreProto(globalData, "__proto__") + , thisIdentifier(globalData, "this") + , useStrictIdentifier(globalData, "use strict") + JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) +{ +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h new file mode 100644 index 0000000..1e22b6a --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2003, 2007, 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 + * 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. + * + */ + +#ifndef CommonIdentifiers_h +#define CommonIdentifiers_h + +#include "Identifier.h" +#include <wtf/Noncopyable.h> + +// MarkedArgumentBuffer of property names, passed to a macro so we can do set them up various +// ways without repeating the list. +#define JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \ + macro(__defineGetter__) \ + macro(__defineSetter__) \ + macro(__lookupGetter__) \ + macro(__lookupSetter__) \ + macro(apply) \ + macro(arguments) \ + macro(call) \ + 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(getOwnPropertyNames) \ + macro(hasOwnProperty) \ + macro(ignoreCase) \ + macro(index) \ + macro(input) \ + macro(isArray) \ + macro(isPrototypeOf) \ + macro(keys) \ + macro(length) \ + macro(message) \ + macro(multiline) \ + macro(name) \ + macro(now) \ + macro(parse) \ + macro(propertyIsEnumerable) \ + macro(prototype) \ + macro(set) \ + macro(source) \ + macro(test) \ + macro(toExponential) \ + macro(toFixed) \ + macro(toISOString) \ + macro(toJSON) \ + macro(toLocaleString) \ + macro(toPrecision) \ + macro(toString) \ + macro(UTC) \ + macro(value) \ + macro(valueOf) \ + macro(writable) \ + macro(displayName) + +namespace JSC { + + class CommonIdentifiers : public Noncopyable { + private: + CommonIdentifiers(JSGlobalData*); + friend class JSGlobalData; + + public: + const Identifier nullIdentifier; + const Identifier emptyIdentifier; + const Identifier underscoreProto; + const Identifier thisIdentifier; + const Identifier useStrictIdentifier; + +#define JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL(name) const Identifier name; + JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL) +#undef JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL + }; + +} // namespace JSC + +#endif // CommonIdentifiers_h diff --git a/Source/JavaScriptCore/runtime/Completion.cpp b/Source/JavaScriptCore/runtime/Completion.cpp new file mode 100644 index 0000000..eeb8b0d --- /dev/null +++ b/Source/JavaScriptCore/runtime/Completion.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007 Apple Inc. + * + * 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 "Completion.h" + +#include "CallFrame.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "Interpreter.h" +#include "Parser.h" +#include "Debugger.h" +#include "WTFThreadData.h" +#include <stdio.h> + +namespace JSC { + +Completion checkSyntax(ExecState* exec, const SourceCode& source) +{ + JSLock lock(exec); + ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable()); + + RefPtr<ProgramExecutable> program = ProgramExecutable::create(exec, source); + JSObject* error = program->checkSyntax(exec); + if (error) + return Completion(Throw, error); + + return Completion(Normal); +} + +Completion evaluate(ExecState* exec, ScopeChain& scopeChain, const SourceCode& source, JSValue thisValue) +{ + JSLock lock(exec); + ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable()); + + 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 result = exec->interpreter()->execute(program.get(), exec, scopeChain.node(), thisObj); + + if (exec->hadException()) { + JSValue exception = exec->exception(); + exec->clearException(); + + ComplType exceptionType = Throw; + if (exception.isObject()) + exceptionType = asObject(exception)->exceptionType(); + return Completion(exceptionType, exception); + } + return Completion(Normal, result); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Completion.h b/Source/JavaScriptCore/runtime/Completion.h new file mode 100644 index 0000000..63b315e --- /dev/null +++ b/Source/JavaScriptCore/runtime/Completion.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007 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 + * 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. + * + */ + +#ifndef Completion_h +#define Completion_h + +#include "JSValue.h" + +namespace JSC { + + class ExecState; + class ScopeChain; + class SourceCode; + + enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted, Terminated }; + + /* + * Completion objects are used to convey the return status and value + * from functions. + */ + class Completion { + public: + Completion(ComplType type = Normal, JSValue value = JSValue()) + : m_type(type) + , m_value(value) + { + } + + ComplType complType() const { return m_type; } + JSValue value() const { return m_value; } + void setValue(JSValue v) { m_value = v; } + bool isValueCompletion() const { return m_value; } + + private: + ComplType m_type; + JSValue m_value; + }; + + Completion checkSyntax(ExecState*, const SourceCode&); + Completion evaluate(ExecState*, ScopeChain&, const SourceCode&, JSValue thisValue = JSValue()); + +} // namespace JSC + +#endif // Completion_h diff --git a/Source/JavaScriptCore/runtime/ConstructData.cpp b/Source/JavaScriptCore/runtime/ConstructData.cpp new file mode 100644 index 0000000..5da2a91 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstructData.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 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 "ConstructData.h" + +#include "Executable.h" +#include "Interpreter.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" + +namespace JSC { + +JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args) +{ + ASSERT(constructType == ConstructTypeJS || constructType == ConstructTypeHost); + return exec->interpreter()->executeConstruct(exec, asObject(constructorObject), constructType, constructData, args); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ConstructData.h b/Source/JavaScriptCore/runtime/ConstructData.h new file mode 100644 index 0000000..3d5f732 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstructData.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 ConstructData_h +#define ConstructData_h + +#include "JSValue.h" + +namespace JSC { + + class ArgList; + class ExecState; + class FunctionExecutable; + class JSObject; + class ScopeChainNode; + + enum ConstructType { + ConstructTypeNone, + ConstructTypeHost, + ConstructTypeJS + }; + + typedef EncodedJSValue (JSC_HOST_CALL *NativeConstructor)(ExecState*); + + union ConstructData { + struct { + NativeConstructor function; + } native; + struct { + FunctionExecutable* functionExecutable; + ScopeChainNode* scopeChain; + } js; + }; + + JSObject* construct(ExecState*, JSValue constructor, ConstructType, const ConstructData&, const ArgList&); + +} // namespace JSC + +#endif // ConstructData_h diff --git a/Source/JavaScriptCore/runtime/DateConstructor.cpp b/Source/JavaScriptCore/runtime/DateConstructor.cpp new file mode 100644 index 0000000..dcbe12d --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConstructor.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "DateConstructor.h" + +#include "DateConversion.h" +#include "DateInstance.h" +#include "DatePrototype.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "ObjectPrototype.h" +#include "PrototypeFunction.h" +#include <math.h> +#include <time.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> + +#if OS(WINCE) && !PLATFORM(QT) +extern "C" time_t time(time_t* timer); // Provided by libce. +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +using namespace WTF; + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(DateConstructor); + +static EncodedJSValue JSC_HOST_CALL dateParse(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateNow(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateUTC(ExecState*); + +DateConstructor::DateConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, DatePrototype* datePrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, datePrototype->classInfo()->className)) +{ + putDirectWithoutTransition(exec->propertyNames().prototype, datePrototype, DontEnum|DontDelete|ReadOnly); + + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().parse, dateParse), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 7, exec->propertyNames().UTC, dateUTC), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().now, dateNow), DontEnum); + + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(7), ReadOnly | DontEnum | DontDelete); +} + +// ECMA 15.9.3 +JSObject* constructDate(ExecState* exec, const ArgList& args) +{ + int numArgs = args.size(); + + double value; + + if (numArgs == 0) // new Date() ECMA 15.9.3.3 + value = jsCurrentTime(); + else if (numArgs == 1) { + if (args.at(0).inherits(&DateInstance::info)) + value = asDateInstance(args.at(0))->internalNumber(); + else { + JSValue primitive = args.at(0).toPrimitive(exec); + if (primitive.isString()) + value = parseDate(exec, primitive.getString(exec)); + else + value = primitive.toNumber(exec); + } + } else { + double doubleArguments[7] = { + args.at(0).toNumber(exec), + args.at(1).toNumber(exec), + args.at(2).toNumber(exec), + args.at(3).toNumber(exec), + args.at(4).toNumber(exec), + args.at(5).toNumber(exec), + args.at(6).toNumber(exec) + }; + if (isnan(doubleArguments[0]) + || isnan(doubleArguments[1]) + || (numArgs >= 3 && isnan(doubleArguments[2])) + || (numArgs >= 4 && isnan(doubleArguments[3])) + || (numArgs >= 5 && isnan(doubleArguments[4])) + || (numArgs >= 6 && isnan(doubleArguments[5])) + || (numArgs >= 7 && isnan(doubleArguments[6]))) + value = NaN; + else { + GregorianDateTime t; + int year = JSC::toInt32(doubleArguments[0]); + t.year = (year >= 0 && year <= 99) ? year : year - 1900; + t.month = JSC::toInt32(doubleArguments[1]); + t.monthDay = (numArgs >= 3) ? JSC::toInt32(doubleArguments[2]) : 1; + t.hour = JSC::toInt32(doubleArguments[3]); + t.minute = JSC::toInt32(doubleArguments[4]); + t.second = JSC::toInt32(doubleArguments[5]); + t.isDST = -1; + double ms = (numArgs >= 7) ? doubleArguments[6] : 0; + value = gregorianDateTimeToMS(exec, t, ms, false); + } + } + + return new (exec) DateInstance(exec, value); +} + +static EncodedJSValue JSC_HOST_CALL constructWithDateConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructDate(exec, args)); +} + +ConstructType DateConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithDateConstructor; + return ConstructTypeHost; +} + +// ECMA 15.9.2 +static EncodedJSValue JSC_HOST_CALL callDate(ExecState* exec) +{ + time_t localTime = time(0); + tm localTM; + getLocalTime(&localTime, &localTM); + GregorianDateTime ts(exec, localTM); + DateConversionBuffer date; + DateConversionBuffer time; + formatDate(ts, date); + formatTime(ts, time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); +} + +CallType DateConstructor::getCallData(CallData& callData) +{ + callData.native.function = callDate; + return CallTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL dateParse(ExecState* exec) +{ + return JSValue::encode(jsNumber(parseDate(exec, exec->argument(0).toString(exec)))); +} + +static EncodedJSValue JSC_HOST_CALL dateNow(ExecState*) +{ + return JSValue::encode(jsNumber(jsCurrentTime())); +} + +static EncodedJSValue JSC_HOST_CALL dateUTC(ExecState* exec) +{ + double doubleArguments[7] = { + exec->argument(0).toNumber(exec), + exec->argument(1).toNumber(exec), + exec->argument(2).toNumber(exec), + exec->argument(3).toNumber(exec), + exec->argument(4).toNumber(exec), + exec->argument(5).toNumber(exec), + exec->argument(6).toNumber(exec) + }; + int n = exec->argumentCount(); + if (isnan(doubleArguments[0]) + || isnan(doubleArguments[1]) + || (n >= 3 && isnan(doubleArguments[2])) + || (n >= 4 && isnan(doubleArguments[3])) + || (n >= 5 && isnan(doubleArguments[4])) + || (n >= 6 && isnan(doubleArguments[5])) + || (n >= 7 && isnan(doubleArguments[6]))) + return JSValue::encode(jsNaN()); + + GregorianDateTime t; + int year = JSC::toInt32(doubleArguments[0]); + t.year = (year >= 0 && year <= 99) ? year : year - 1900; + t.month = JSC::toInt32(doubleArguments[1]); + t.monthDay = (n >= 3) ? JSC::toInt32(doubleArguments[2]) : 1; + t.hour = JSC::toInt32(doubleArguments[3]); + t.minute = JSC::toInt32(doubleArguments[4]); + t.second = JSC::toInt32(doubleArguments[5]); + double ms = (n >= 7) ? doubleArguments[6] : 0; + return JSValue::encode(jsNumber(timeClip(gregorianDateTimeToMS(exec, t, ms, true)))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DateConstructor.h b/Source/JavaScriptCore/runtime/DateConstructor.h new file mode 100644 index 0000000..c8ca456 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConstructor.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DateConstructor_h +#define DateConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class DatePrototype; + + class DateConstructor : public InternalFunction { + public: + DateConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, DatePrototype*); + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + + JSObject* constructDate(ExecState*, const ArgList&); + +} // namespace JSC + +#endif // DateConstructor_h diff --git a/Source/JavaScriptCore/runtime/DateConversion.cpp b/Source/JavaScriptCore/runtime/DateConversion.cpp new file mode 100644 index 0000000..d4b8232 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConversion.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. 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 + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "DateConversion.h" + +#include "CallFrame.h" +#include "UString.h" +#include <wtf/DateMath.h> +#include <wtf/StringExtras.h> +#include <wtf/text/CString.h> + +using namespace WTF; + +namespace JSC { + +double parseDate(ExecState* exec, const UString &date) +{ + if (date == exec->globalData().cachedDateString) + return exec->globalData().cachedDateStringValue; + double value = parseES5DateFromNullTerminatedCharacters(date.utf8().data()); + if (isnan(value)) + value = parseDateFromNullTerminatedCharacters(exec, date.utf8().data()); + exec->globalData().cachedDateString = date; + exec->globalData().cachedDateStringValue = value; + return value; +} + +void formatDate(const GregorianDateTime &t, DateConversionBuffer& buffer) +{ + snprintf(buffer, DateConversionBufferSize, "%s %s %02d %04d", + weekdayName[(t.weekDay + 6) % 7], + monthName[t.month], t.monthDay, t.year + 1900); +} + +void formatDateUTCVariant(const GregorianDateTime &t, DateConversionBuffer& buffer) +{ + snprintf(buffer, DateConversionBufferSize, "%s, %02d %s %04d", + weekdayName[(t.weekDay + 6) % 7], + t.monthDay, monthName[t.month], t.year + 1900); +} + +void formatTime(const GregorianDateTime &t, DateConversionBuffer& buffer) +{ + int offset = abs(gmtoffset(t)); + char timeZoneName[70]; + struct tm gtm = t; + strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); + + if (timeZoneName[0]) { + snprintf(buffer, DateConversionBufferSize, "%02d:%02d:%02d GMT%c%02d%02d (%s)", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, timeZoneName); + } else { + snprintf(buffer, DateConversionBufferSize, "%02d:%02d:%02d GMT%c%02d%02d", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60); + } +} + +void formatTimeUTC(const GregorianDateTime &t, DateConversionBuffer& buffer) +{ + snprintf(buffer, DateConversionBufferSize, "%02d:%02d:%02d GMT", t.hour, t.minute, t.second); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DateConversion.h b/Source/JavaScriptCore/runtime/DateConversion.h new file mode 100644 index 0000000..ff32b50 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConversion.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + */ + +#ifndef DateConversion_h +#define DateConversion_h + +#include "UString.h" + +namespace JSC { + +class ExecState; +struct GregorianDateTime; + +static const unsigned DateConversionBufferSize = 100; +typedef char DateConversionBuffer[DateConversionBufferSize]; + +double parseDate(ExecState* exec, const UString&); +void formatDate(const GregorianDateTime&, DateConversionBuffer&); +void formatDateUTCVariant(const GregorianDateTime&, DateConversionBuffer&); +void formatTime(const GregorianDateTime&, DateConversionBuffer&); +void formatTimeUTC(const GregorianDateTime&, DateConversionBuffer&); + +} // namespace JSC + +#endif // DateConversion_h diff --git a/Source/JavaScriptCore/runtime/DateInstance.cpp b/Source/JavaScriptCore/runtime/DateInstance.cpp new file mode 100644 index 0000000..8562e2d --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstance.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "DateInstance.h" + +#include "JSGlobalObject.h" + +#include <math.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> + +using namespace WTF; + +namespace JSC { + +const ClassInfo DateInstance::info = {"Date", 0, 0, 0}; + +DateInstance::DateInstance(ExecState*, NonNullPassRefPtr<Structure> structure) + : JSWrapperObject(structure) +{ + setInternalValue(jsNaN()); +} + +DateInstance::DateInstance(ExecState*, NonNullPassRefPtr<Structure> structure, double time) + : JSWrapperObject(structure) +{ + setInternalValue(jsNumber(timeClip(time))); +} + +DateInstance::DateInstance(ExecState* exec, double time) + : JSWrapperObject(exec->lexicalGlobalObject()->dateStructure()) +{ + setInternalValue(jsNumber(timeClip(time))); +} + +const GregorianDateTime* DateInstance::calculateGregorianDateTime(ExecState* exec) const +{ + double milli = internalNumber(); + if (isnan(milli)) + return 0; + + if (!m_data) + m_data = exec->globalData().dateInstanceCache.add(milli); + + if (m_data->m_gregorianDateTimeCachedForMS != milli) { + msToGregorianDateTime(exec, milli, false, m_data->m_cachedGregorianDateTime); + m_data->m_gregorianDateTimeCachedForMS = milli; + } + return &m_data->m_cachedGregorianDateTime; +} + +const GregorianDateTime* DateInstance::calculateGregorianDateTimeUTC(ExecState* exec) const +{ + double milli = internalNumber(); + if (isnan(milli)) + return 0; + + if (!m_data) + m_data = exec->globalData().dateInstanceCache.add(milli); + + if (m_data->m_gregorianDateTimeUTCCachedForMS != milli) { + msToGregorianDateTime(exec, milli, true, m_data->m_cachedGregorianDateTimeUTC); + m_data->m_gregorianDateTimeUTCCachedForMS = milli; + } + return &m_data->m_cachedGregorianDateTimeUTC; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DateInstance.h b/Source/JavaScriptCore/runtime/DateInstance.h new file mode 100644 index 0000000..77d46de --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstance.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DateInstance_h +#define DateInstance_h + +#include "JSWrapperObject.h" + +namespace WTF { + struct GregorianDateTime; +} + +namespace JSC { + + class DateInstance : public JSWrapperObject { + public: + DateInstance(ExecState*, double); + DateInstance(ExecState*, NonNullPassRefPtr<Structure>, double); + explicit DateInstance(ExecState*, NonNullPassRefPtr<Structure>); + + double internalNumber() const { return internalValue().uncheckedGetNumber(); } + + static JS_EXPORTDATA const ClassInfo info; + + const GregorianDateTime* gregorianDateTime(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTime; + return calculateGregorianDateTime(exec); + } + + const GregorianDateTime* gregorianDateTimeUTC(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeUTCCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTimeUTC; + return calculateGregorianDateTimeUTC(exec); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesMarkChildren | JSWrapperObject::StructureFlags; + + private: + const GregorianDateTime* calculateGregorianDateTime(ExecState*) const; + const GregorianDateTime* calculateGregorianDateTimeUTC(ExecState*) const; + virtual const ClassInfo* classInfo() const { return &info; } + + mutable RefPtr<DateInstanceData> m_data; + }; + + DateInstance* asDateInstance(JSValue); + + inline DateInstance* asDateInstance(JSValue value) + { + ASSERT(asObject(value)->inherits(&DateInstance::info)); + return static_cast<DateInstance*>(asObject(value)); + } + +} // namespace JSC + +#endif // DateInstance_h diff --git a/Source/JavaScriptCore/runtime/DateInstanceCache.h b/Source/JavaScriptCore/runtime/DateInstanceCache.h new file mode 100644 index 0000000..b60c29a --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstanceCache.h @@ -0,0 +1,94 @@ +/* + * 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 DateInstanceCache_h +#define DateInstanceCache_h + +#include <wtf/DateMath.h> +#include <wtf/HashFunctions.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace JSC { + + extern const double NaN; + + class DateInstanceData : public RefCounted<DateInstanceData> { + public: + static PassRefPtr<DateInstanceData> create() { return adoptRef(new DateInstanceData); } + + double m_gregorianDateTimeCachedForMS; + GregorianDateTime m_cachedGregorianDateTime; + double m_gregorianDateTimeUTCCachedForMS; + GregorianDateTime m_cachedGregorianDateTimeUTC; + + private: + DateInstanceData() + : m_gregorianDateTimeCachedForMS(NaN) + , m_gregorianDateTimeUTCCachedForMS(NaN) + { + } + }; + + class DateInstanceCache { + public: + DateInstanceCache() + { + reset(); + } + + void reset() + { + for (size_t i = 0; i < cacheSize; ++i) + m_cache[i].key = NaN; + } + + DateInstanceData* add(double d) + { + CacheEntry& entry = lookup(d); + if (d == entry.key) + return entry.value.get(); + + entry.key = d; + entry.value = DateInstanceData::create(); + return entry.value.get(); + } + + private: + static const size_t cacheSize = 16; + + struct CacheEntry { + double key; + RefPtr<DateInstanceData> value; + }; + + CacheEntry& lookup(double d) { return m_cache[WTF::FloatHash<double>::hash(d) & (cacheSize - 1)]; } + + FixedArray<CacheEntry, cacheSize> m_cache; + }; + +} // namespace JSC + +#endif // DateInstanceCache_h diff --git a/Source/JavaScriptCore/runtime/DatePrototype.cpp b/Source/JavaScriptCore/runtime/DatePrototype.cpp new file mode 100644 index 0000000..085cb33 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DatePrototype.cpp @@ -0,0 +1,1094 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile, Inc. All rights reserved. + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "DatePrototype.h" + +#include "DateConversion.h" +#include "DateInstance.h" +#include "Error.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Lookup.h" +#include "ObjectPrototype.h" + +#if !PLATFORM(MAC) && HAVE(LANGINFO_H) +#include <langinfo.h> +#endif + +#include <limits.h> +#include <locale.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> +#include <wtf/Assertions.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/UnusedParam.h> + +#if HAVE(SYS_PARAM_H) +#include <sys/param.h> +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +#if PLATFORM(MAC) +#include <CoreFoundation/CoreFoundation.h> +#endif + +#if OS(WINCE) && !PLATFORM(QT) +extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); //provided by libce +#endif + +using namespace WTF; + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(DatePrototype); + +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState*); + +} + +#include "DatePrototype.lut.h" + +namespace JSC { + +enum LocaleDateTimeFormat { LocaleDateAndTime, LocaleDate, LocaleTime }; + +#if PLATFORM(MAC) + +// FIXME: Since this is superior to the strftime-based version, why limit this to PLATFORM(MAC)? +// Instead we should consider using this whenever PLATFORM(CF) is true. + +static CFDateFormatterStyle styleFromArgString(const UString& string, CFDateFormatterStyle defaultStyle) +{ + if (string == "short") + return kCFDateFormatterShortStyle; + if (string == "medium") + return kCFDateFormatterMediumStyle; + if (string == "long") + return kCFDateFormatterLongStyle; + if (string == "full") + return kCFDateFormatterFullStyle; + return defaultStyle; +} + +static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMilliseconds, LocaleDateTimeFormat format) +{ + CFDateFormatterStyle dateStyle = (format != LocaleTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); + CFDateFormatterStyle timeStyle = (format != LocaleDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); + + bool useCustomFormat = false; + UString customFormatString; + + UString arg0String = exec->argument(0).toString(exec); + if (arg0String == "custom" && !exec->argument(1).isUndefined()) { + useCustomFormat = true; + customFormatString = exec->argument(1).toString(exec); + } else if (format == LocaleDateAndTime && !exec->argument(1).isUndefined()) { + dateStyle = styleFromArgString(arg0String, dateStyle); + timeStyle = styleFromArgString(exec->argument(1).toString(exec), timeStyle); + } else if (format != LocaleTime && !exec->argument(0).isUndefined()) + dateStyle = styleFromArgString(arg0String, dateStyle); + else if (format != LocaleDate && !exec->argument(0).isUndefined()) + timeStyle = styleFromArgString(arg0String, timeStyle); + + CFLocaleRef locale = CFLocaleCopyCurrent(); + CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle); + CFRelease(locale); + + if (useCustomFormat) { + CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, customFormatString.characters(), customFormatString.length()); + CFDateFormatterSetFormat(formatter, customFormatCFString); + CFRelease(customFormatCFString); + } + + CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, floor(timeInMilliseconds / msPerSecond) - kCFAbsoluteTimeIntervalSince1970); + + CFRelease(formatter); + + // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters). + // That's not great error handling, but it just won't happen so it doesn't matter. + UChar buffer[200]; + const size_t bufferLength = WTF_ARRAY_LENGTH(buffer); + size_t length = CFStringGetLength(string); + ASSERT(length <= bufferLength); + if (length > bufferLength) + length = bufferLength; + CFStringGetCharacters(string, CFRangeMake(0, length), buffer); + + CFRelease(string); + + return jsNontrivialString(exec, UString(buffer, length)); +} + +#else // !PLATFORM(MAC) + +static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, LocaleDateTimeFormat format) +{ +#if HAVE(LANGINFO_H) + static const nl_item formats[] = { D_T_FMT, D_FMT, T_FMT }; +#elif (OS(WINCE) && !PLATFORM(QT)) || OS(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" }; +#endif + + // Offset year if needed + struct tm localTM = gdt; + int year = gdt.year + 1900; + bool yearNeedsOffset = year < 1900 || year > 2038; + if (yearNeedsOffset) + localTM.tm_year = equivalentYearForDST(year) - 1900; + +#if HAVE(LANGINFO_H) + // We do not allow strftime to generate dates with 2-digits years, + // both to avoid ambiguity, and a crash in strncpy, for years that + // need offset. + char* formatString = strdup(nl_langinfo(formats[format])); + char* yPos = strchr(formatString, 'y'); + if (yPos) + *yPos = 'Y'; +#endif + + // Do the formatting + const int bufsize = 128; + char timebuffer[bufsize]; + +#if HAVE(LANGINFO_H) + size_t ret = strftime(timebuffer, bufsize, formatString, &localTM); + free(formatString); +#else + size_t ret = strftime(timebuffer, bufsize, formatStrings[format], &localTM); +#endif + + if (ret == 0) + return jsEmptyString(exec); + + // Copy original into the buffer + if (yearNeedsOffset && format != LocaleTime) { + static const int yearLen = 5; // FIXME will be a problem in the year 10,000 + char yearString[yearLen]; + + snprintf(yearString, yearLen, "%d", localTM.tm_year + 1900); + char* yearLocation = strstr(timebuffer, yearString); + snprintf(yearString, yearLen, "%d", year); + + strncpy(yearLocation, yearString, yearLen - 1); + } + + // Convert multi-byte result to UNICODE. + // If __STDC_ISO_10646__ is defined, wide character represents + // UTF-16 (or UTF-32) code point. In most modern Unix like system + // (e.g. Linux with glibc 2.2 and above) the macro is defined, + // and wide character represents UTF-32 code point. + // Here we static_cast potential UTF-32 to UTF-16, it should be + // safe because date and (or) time related characters in different languages + // should be in UNICODE BMP. If mbstowcs fails, we just fall + // back on using multi-byte result as-is. +#ifdef __STDC_ISO_10646__ + UChar buffer[bufsize]; + wchar_t tempbuffer[bufsize]; + size_t length = mbstowcs(tempbuffer, timebuffer, bufsize - 1); + if (length != static_cast<size_t>(-1)) { + for (size_t i = 0; i < length; ++i) + buffer[i] = static_cast<UChar>(tempbuffer[i]); + return jsNontrivialString(exec, UString(buffer, length)); + } +#endif + + return jsNontrivialString(exec, timebuffer); +} + +static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, double, LocaleDateTimeFormat format) +{ + const GregorianDateTime* gregorianDateTime = dateObject->gregorianDateTime(exec); + if (!gregorianDateTime) + return jsNontrivialString(exec, "Invalid Date"); + return formatLocaleDate(exec, *gregorianDateTime, format); +} + +#endif // !PLATFORM(MAC) + +// Converts a list of arguments sent to a Date member function into milliseconds, updating +// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. +// +// Format of member function: f([hour,] [min,] [sec,] [ms]) +static bool fillStructuresUsingTimeArgs(ExecState* exec, int maxArgs, double* ms, GregorianDateTime* t) +{ + double milliseconds = 0; + bool ok = true; + int idx = 0; + int numArgs = exec->argumentCount(); + + // JS allows extra trailing arguments -- ignore them + if (numArgs > maxArgs) + numArgs = maxArgs; + + // hours + if (maxArgs >= 4 && idx < numArgs) { + t->hour = 0; + double hours = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(hours); + milliseconds += hours * msPerHour; + } + + // minutes + if (maxArgs >= 3 && idx < numArgs && ok) { + t->minute = 0; + double minutes = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(minutes); + milliseconds += minutes * msPerMinute; + } + + // seconds + if (maxArgs >= 2 && idx < numArgs && ok) { + t->second = 0; + double seconds = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(seconds); + milliseconds += seconds * msPerSecond; + } + + if (!ok) + return false; + + // milliseconds + if (idx < numArgs) { + double millis = exec->argument(idx).toIntegerPreserveNaN(exec); + ok = isfinite(millis); + milliseconds += millis; + } else + milliseconds += *ms; + + *ms = milliseconds; + return ok; +} + +// Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating +// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. +// +// Format of member function: f([years,] [months,] [days]) +static bool fillStructuresUsingDateArgs(ExecState *exec, int maxArgs, double *ms, GregorianDateTime *t) +{ + int idx = 0; + bool ok = true; + int numArgs = exec->argumentCount(); + + // JS allows extra trailing arguments -- ignore them + if (numArgs > maxArgs) + numArgs = maxArgs; + + // years + if (maxArgs >= 3 && idx < numArgs) { + double years = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(years); + t->year = toInt32(years - 1900); + } + // months + if (maxArgs >= 2 && idx < numArgs && ok) { + double months = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(months); + t->month = toInt32(months); + } + // days + if (idx < numArgs && ok) { + double days = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(days); + t->monthDay = 0; + *ms += days * msPerDay; + } + + return ok; +} + +const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, 0, ExecState::dateTable}; + +/* Source for DatePrototype.lut.h +@begin dateTable + toString dateProtoFuncToString DontEnum|Function 0 + toISOString dateProtoFuncToISOString DontEnum|Function 0 + toUTCString dateProtoFuncToUTCString DontEnum|Function 0 + toDateString dateProtoFuncToDateString DontEnum|Function 0 + toTimeString dateProtoFuncToTimeString DontEnum|Function 0 + toLocaleString dateProtoFuncToLocaleString DontEnum|Function 0 + toLocaleDateString dateProtoFuncToLocaleDateString DontEnum|Function 0 + toLocaleTimeString dateProtoFuncToLocaleTimeString DontEnum|Function 0 + valueOf dateProtoFuncGetTime DontEnum|Function 0 + getTime dateProtoFuncGetTime DontEnum|Function 0 + getFullYear dateProtoFuncGetFullYear DontEnum|Function 0 + getUTCFullYear dateProtoFuncGetUTCFullYear DontEnum|Function 0 + toGMTString dateProtoFuncToGMTString DontEnum|Function 0 + getMonth dateProtoFuncGetMonth DontEnum|Function 0 + getUTCMonth dateProtoFuncGetUTCMonth DontEnum|Function 0 + getDate dateProtoFuncGetDate DontEnum|Function 0 + getUTCDate dateProtoFuncGetUTCDate DontEnum|Function 0 + getDay dateProtoFuncGetDay DontEnum|Function 0 + getUTCDay dateProtoFuncGetUTCDay DontEnum|Function 0 + getHours dateProtoFuncGetHours DontEnum|Function 0 + getUTCHours dateProtoFuncGetUTCHours DontEnum|Function 0 + getMinutes dateProtoFuncGetMinutes DontEnum|Function 0 + getUTCMinutes dateProtoFuncGetUTCMinutes DontEnum|Function 0 + getSeconds dateProtoFuncGetSeconds DontEnum|Function 0 + getUTCSeconds dateProtoFuncGetUTCSeconds DontEnum|Function 0 + getMilliseconds dateProtoFuncGetMilliSeconds DontEnum|Function 0 + getUTCMilliseconds dateProtoFuncGetUTCMilliseconds DontEnum|Function 0 + getTimezoneOffset dateProtoFuncGetTimezoneOffset DontEnum|Function 0 + setTime dateProtoFuncSetTime DontEnum|Function 1 + setMilliseconds dateProtoFuncSetMilliSeconds DontEnum|Function 1 + setUTCMilliseconds dateProtoFuncSetUTCMilliseconds DontEnum|Function 1 + setSeconds dateProtoFuncSetSeconds DontEnum|Function 2 + setUTCSeconds dateProtoFuncSetUTCSeconds DontEnum|Function 2 + setMinutes dateProtoFuncSetMinutes DontEnum|Function 3 + setUTCMinutes dateProtoFuncSetUTCMinutes DontEnum|Function 3 + setHours dateProtoFuncSetHours DontEnum|Function 4 + setUTCHours dateProtoFuncSetUTCHours DontEnum|Function 4 + setDate dateProtoFuncSetDate DontEnum|Function 1 + setUTCDate dateProtoFuncSetUTCDate DontEnum|Function 1 + setMonth dateProtoFuncSetMonth DontEnum|Function 2 + setUTCMonth dateProtoFuncSetUTCMonth DontEnum|Function 2 + setFullYear dateProtoFuncSetFullYear DontEnum|Function 3 + setUTCFullYear dateProtoFuncSetUTCFullYear DontEnum|Function 3 + setYear dateProtoFuncSetYear DontEnum|Function 1 + getYear dateProtoFuncGetYear DontEnum|Function 0 + toJSON dateProtoFuncToJSON DontEnum|Function 1 +@end +*/ + +// ECMA 15.9.4 + +DatePrototype::DatePrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : DateInstance(exec, structure) +{ + // The constructor will be added later, after DateConstructor has been built. + putAnonymousValue(0, globalObject); +} + +bool DatePrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + 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 + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + DateConversionBuffer date; + DateConversionBuffer time; + formatDate(*gregorianDateTime, date); + formatTime(*gregorianDateTime, time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + DateConversionBuffer date; + DateConversionBuffer time; + formatDateUTCVariant(*gregorianDateTime, date); + formatTimeUTC(*gregorianDateTime, time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + // 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 + gregorianDateTime->year, gregorianDateTime->month + 1, gregorianDateTime->monthDay, gregorianDateTime->hour, gregorianDateTime->minute, gregorianDateTime->second, static_cast<int>(fmod(thisDateObj->internalNumber(), 1000))); + buffer[sizeof(buffer) - 1] = 0; + return JSValue::encode(jsNontrivialString(exec, buffer)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + DateConversionBuffer date; + formatDate(*gregorianDateTime, date); + return JSValue::encode(jsNontrivialString(exec, date)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + DateConversionBuffer time; + formatTime(*gregorianDateTime, time); + return JSValue::encode(jsNontrivialString(exec, time)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDateAndTime)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDate)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleTime)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + return JSValue::encode(asDateInstance(thisValue)->internalValue()); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(1900 + gregorianDateTime->year)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(1900 + gregorianDateTime->year)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + DateConversionBuffer date; + DateConversionBuffer time; + formatDateUTCVariant(*gregorianDateTime, date); + formatTimeUTC(*gregorianDateTime, time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->month)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->month)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->monthDay)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->monthDay)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->weekDay)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->weekDay)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->hour)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->hour)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->minute)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->minute)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->second)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->second)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + if (isnan(milli)) + return JSValue::encode(jsNaN()); + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + return JSValue::encode(jsNumber(ms)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + if (isnan(milli)) + return JSValue::encode(jsNaN()); + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + return JSValue::encode(jsNumber(ms)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(-gregorianDateTime->utcOffset / minutesPerHour)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + double milli = timeClip(exec->argument(0).toNumber(exec)); + JSValue result = jsNumber(milli); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); +} + +static EncodedJSValue setNewValueFromTimeArgs(ExecState* exec, int numArgsToUse, bool inputIsUTC) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + + if (!exec->argumentCount() || isnan(milli)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); + } + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + + const GregorianDateTime* other = inputIsUTC + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return JSValue::encode(jsNaN()); + + GregorianDateTime gregorianDateTime; + gregorianDateTime.copyFrom(*other); + if (!fillStructuresUsingTimeArgs(exec, numArgsToUse, &ms, &gregorianDateTime)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); + } + + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); +} + +static EncodedJSValue setNewValueFromDateArgs(ExecState* exec, int numArgsToUse, bool inputIsUTC) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); + } + + double milli = thisDateObj->internalNumber(); + double ms = 0; + + GregorianDateTime gregorianDateTime; + if (numArgsToUse == 3 && isnan(milli)) + msToGregorianDateTime(exec, 0, true, gregorianDateTime); + else { + ms = milli - floor(milli / msPerSecond) * msPerSecond; + const GregorianDateTime* other = inputIsUTC + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return JSValue::encode(jsNaN()); + gregorianDateTime.copyFrom(*other); + } + + if (!fillStructuresUsingDateArgs(exec, numArgsToUse, &ms, &gregorianDateTime)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); + } + + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, 1, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, 1, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, 2, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, 2, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, 3, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, 3, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, 4, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, 4, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromDateArgs(exec, 1, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromDateArgs(exec, 1, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromDateArgs(exec, 2, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromDateArgs(exec, 2, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState* exec) +{ + const bool inputIsUTC = false; + return setNewValueFromDateArgs(exec, 3, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState* exec) +{ + const bool inputIsUTC = true; + return setNewValueFromDateArgs(exec, 3, inputIsUTC); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); + } + + double milli = thisDateObj->internalNumber(); + double ms = 0; + + GregorianDateTime gregorianDateTime; + if (isnan(milli)) + // Based on ECMA 262 B.2.5 (setYear) + // the time must be reset to +0 if it is NaN. + msToGregorianDateTime(exec, 0, true, gregorianDateTime); + else { + double secs = floor(milli / msPerSecond); + ms = milli - secs * msPerSecond; + if (const GregorianDateTime* other = thisDateObj->gregorianDateTime(exec)) + gregorianDateTime.copyFrom(*other); + } + + double year = exec->argument(0).toIntegerPreserveNaN(exec); + if (!isfinite(year)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); + } + + gregorianDateTime.year = toInt32((year > 99 || year < 0) ? year - 1900 : year); + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, false)); + thisDateObj->setInternalValue(result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + + // NOTE: IE returns the full year even in getYear. + return JSValue::encode(jsNumber(gregorianDateTime->year)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSObject* object = thisValue.toThisObject(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + + JSValue toISOValue = object->get(exec, exec->globalData().propertyNames->toISOString); + if (exec->hadException()) + return JSValue::encode(jsNull()); + + CallData callData; + CallType callType = getCallData(toISOValue, callData); + if (callType == CallTypeNone) + return throwVMError(exec, createTypeError(exec, "toISOString is not a function")); + + JSValue result = call(exec, asObject(toISOValue), callType, callData, object, exec->emptyList()); + if (exec->hadException()) + return JSValue::encode(jsNull()); + if (result.isObject()) + return throwVMError(exec, createTypeError(exec, "toISOString did not return a primitive value")); + return JSValue::encode(result); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DatePrototype.h b/Source/JavaScriptCore/runtime/DatePrototype.h new file mode 100644 index 0000000..e3672aa --- /dev/null +++ b/Source/JavaScriptCore/runtime/DatePrototype.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DatePrototype_h +#define DatePrototype_h + +#include "DateInstance.h" + +namespace JSC { + + class ObjectPrototype; + + class DatePrototype : public DateInstance { + public: + DatePrototype(ExecState*, JSGlobalObject*, 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, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | DateInstance::StructureFlags; + + }; + +} // namespace JSC + +#endif // DatePrototype_h diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp new file mode 100644 index 0000000..227e9ec --- /dev/null +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel (eric@webkit.org) + * + * 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 "Error.h" + +#include "ConstructData.h" +#include "ErrorConstructor.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSObject.h" +#include "JSString.h" +#include "NativeErrorConstructor.h" +#include "SourceCode.h" + +namespace JSC { + +static const char* linePropertyName = "line"; +static const char* sourceIdPropertyName = "sourceId"; +static const char* sourceURLPropertyName = "sourceURL"; + +JSObject* createError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->errorStructure(), message); +} + +JSObject* createEvalError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->evalErrorConstructor()->errorStructure(), message); +} + +JSObject* createRangeError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->rangeErrorConstructor()->errorStructure(), message); +} + +JSObject* createReferenceError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->referenceErrorConstructor()->errorStructure(), message); +} + +JSObject* createSyntaxError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->syntaxErrorConstructor()->errorStructure(), message); +} + +JSObject* createTypeError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->typeErrorConstructor()->errorStructure(), message); +} + +JSObject* createURIError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->URIErrorConstructor()->errorStructure(), message); +} + +JSObject* createError(ExecState* exec, const UString& message) +{ + return createError(exec->lexicalGlobalObject(), message); +} + +JSObject* createEvalError(ExecState* exec, const UString& message) +{ + return createEvalError(exec->lexicalGlobalObject(), message); +} + +JSObject* createRangeError(ExecState* exec, const UString& message) +{ + return createRangeError(exec->lexicalGlobalObject(), message); +} + +JSObject* createReferenceError(ExecState* exec, const UString& message) +{ + return createReferenceError(exec->lexicalGlobalObject(), message); +} + +JSObject* createSyntaxError(ExecState* exec, const UString& message) +{ + return createSyntaxError(exec->lexicalGlobalObject(), message); +} + +JSObject* createTypeError(ExecState* exec, const UString& message) +{ + return createTypeError(exec->lexicalGlobalObject(), message); +} + +JSObject* createURIError(ExecState* exec, const UString& message) +{ + return createURIError(exec->lexicalGlobalObject(), message); +} + +JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source) +{ + intptr_t sourceID = source.provider()->asID(); + const UString& sourceURL = source.provider()->url(); + + if (line != -1) + error->putWithAttributes(globalData, Identifier(globalData, linePropertyName), jsNumber(line), ReadOnly | DontDelete); + if (sourceID != -1) + error->putWithAttributes(globalData, Identifier(globalData, sourceIdPropertyName), jsNumber((double)sourceID), ReadOnly | DontDelete); + if (!sourceURL.isNull()) + error->putWithAttributes(globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete); + + return error; +} + +JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source) +{ + return addErrorInfo(&exec->globalData(), error, line, source); +} + +bool hasErrorInfo(ExecState* exec, JSObject* error) +{ + return error->hasProperty(exec, Identifier(exec, linePropertyName)) + || error->hasProperty(exec, Identifier(exec, sourceIdPropertyName)) + || error->hasProperty(exec, Identifier(exec, sourceURLPropertyName)); +} + +JSValue throwError(ExecState* exec, JSValue error) +{ + exec->globalData().exception = error; + return error; +} + +JSObject* throwError(ExecState* exec, JSObject* error) +{ + exec->globalData().exception = error; + return error; +} + +JSObject* throwTypeError(ExecState* exec) +{ + return throwError(exec, createTypeError(exec, "Type error")); +} + +JSObject* throwSyntaxError(ExecState* exec) +{ + return throwError(exec, createSyntaxError(exec, "Syntax error")); +} + +class StrictModeTypeErrorFunction : public InternalFunction { +public: + StrictModeTypeErrorFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const UString& message) + : InternalFunction(&exec->globalData(), globalObject, structure, exec->globalData().propertyNames->emptyIdentifier) + , m_message(message) + { + } + + static EncodedJSValue JSC_HOST_CALL constructThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + ConstructType getConstructData(ConstructData& constructData) + { + constructData.native.function = constructThrowTypeError; + return ConstructTypeHost; + } + + static EncodedJSValue JSC_HOST_CALL callThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + CallType getCallData(CallData& callData) + { + callData.native.function = callThrowTypeError; + return CallTypeHost; + } + +private: + UString m_message; +}; + +COMPILE_ASSERT(sizeof(StrictModeTypeErrorFunction) <= sizeof(CollectorCell), sizeof_StrictModeTypeErrorFunction_must_be_less_than_CollectorCell); + +JSValue createTypeErrorFunction(ExecState* exec, const UString& message) +{ + return new (exec) StrictModeTypeErrorFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->internalFunctionStructure(), message); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h new file mode 100644 index 0000000..c0f9d32 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Error.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 + * 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. + * + */ + +#ifndef Error_h +#define Error_h + +#include "JSObject.h" +#include <stdint.h> + +namespace JSC { + + class ExecState; + class JSGlobalData; + class JSGlobalObject; + class JSObject; + class SourceCode; + class Structure; + class UString; + + // Methods to create a range of internal errors. + JSObject* createError(JSGlobalObject*, const UString&); + JSObject* createEvalError(JSGlobalObject*, const UString&); + JSObject* createRangeError(JSGlobalObject*, const UString&); + JSObject* createReferenceError(JSGlobalObject*, const UString&); + JSObject* createSyntaxError(JSGlobalObject*, const UString&); + JSObject* createTypeError(JSGlobalObject*, const UString&); + JSObject* createURIError(JSGlobalObject*, const UString&); + // ExecState wrappers. + JSObject* createError(ExecState*, const UString&); + JSObject* createEvalError(ExecState*, const UString&); + JSObject* createRangeError(ExecState*, const UString&); + JSObject* createReferenceError(ExecState*, const UString&); + JSObject* createSyntaxError(ExecState*, const UString&); + JSObject* createTypeError(ExecState*, const UString&); + JSObject* createURIError(ExecState*, const UString&); + + // Methods to add + bool hasErrorInfo(ExecState*, JSObject* error); + JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&); + // ExecState wrappers. + JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&); + + // Methods to throw Errors. + JSValue throwError(ExecState*, JSValue); + JSObject* throwError(ExecState*, JSObject*); + + // Convenience wrappers, create an throw an exception with a default message. + JSObject* throwTypeError(ExecState*); + JSObject* throwSyntaxError(ExecState*); + + // Convenience wrappers, wrap result as an EncodedJSValue. + inline EncodedJSValue throwVMError(ExecState* exec, JSValue error) { return JSValue::encode(throwError(exec, error)); } + inline EncodedJSValue throwVMTypeError(ExecState* exec) { return JSValue::encode(throwTypeError(exec)); } + + JSValue createTypeErrorFunction(ExecState* exec, const UString& message); + +} // namespace JSC + +#endif // Error_h diff --git a/Source/JavaScriptCore/runtime/ErrorConstructor.cpp b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp new file mode 100644 index 0000000..4326a4d --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ErrorConstructor.h" + +#include "ErrorPrototype.h" +#include "JSGlobalObject.h" +#include "JSString.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ErrorConstructor); + +ErrorConstructor::ErrorConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ErrorPrototype* errorPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, errorPrototype->classInfo()->className)) +{ + // ECMA 15.11.3.1 Error.prototype + putDirectWithoutTransition(exec->propertyNames().prototype, errorPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), DontDelete | ReadOnly | DontEnum); +} + +// ECMA 15.9.3 + +static EncodedJSValue JSC_HOST_CALL constructWithErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = exec->lexicalGlobalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +ConstructType ErrorConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithErrorConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = exec->lexicalGlobalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +CallType ErrorConstructor::getCallData(CallData& callData) +{ + callData.native.function = callErrorConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorConstructor.h b/Source/JavaScriptCore/runtime/ErrorConstructor.h new file mode 100644 index 0000000..3d0d706 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ErrorConstructor_h +#define ErrorConstructor_h + +#include "ErrorInstance.h" +#include "InternalFunction.h" + +namespace JSC { + + class ErrorPrototype; + + class ErrorConstructor : public InternalFunction { + public: + ErrorConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, ErrorPrototype*); + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + +} // namespace JSC + +#endif // ErrorConstructor_h diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.cpp b/Source/JavaScriptCore/runtime/ErrorInstance.cpp new file mode 100644 index 0000000..0f3153c --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorInstance.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ErrorInstance.h" + +namespace JSC { + +const ClassInfo ErrorInstance::info = { "Error", 0, 0, 0 }; + +ErrorInstance::ErrorInstance(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure) + : JSObject(structure) + , m_appendSourceToMessage(false) +{ + putDirect(globalData->propertyNames->message, jsString(globalData, "")); +} + +ErrorInstance::ErrorInstance(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure, const UString& message) + : JSObject(structure) + , m_appendSourceToMessage(false) +{ + putDirect(globalData->propertyNames->message, jsString(globalData, message)); +} + +ErrorInstance* ErrorInstance::create(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure, const UString& message) +{ + return new (globalData) ErrorInstance(globalData, structure, message); +} + +ErrorInstance* ErrorInstance::create(ExecState* exec, NonNullPassRefPtr<Structure> structure, JSValue message) +{ + if (message.isUndefined()) + return new (exec) ErrorInstance(&exec->globalData(), structure); + return new (exec) ErrorInstance(&exec->globalData(), structure, message.toString(exec)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.h b/Source/JavaScriptCore/runtime/ErrorInstance.h new file mode 100644 index 0000000..b3bebec --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorInstance.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ErrorInstance_h +#define ErrorInstance_h + +#include "JSObject.h" + +namespace JSC { + + class ErrorInstance : public JSObject { + public: + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + static ErrorInstance* create(JSGlobalData*, NonNullPassRefPtr<Structure>, const UString&); + static ErrorInstance* create(ExecState* exec, NonNullPassRefPtr<Structure>, JSValue message); + + + bool appendSourceToMessage() { return m_appendSourceToMessage; } + void setAppendSourceToMessage() { m_appendSourceToMessage = true; } + void clearAppendSourceToMessage() { m_appendSourceToMessage = false; } + + virtual bool isErrorInstance() const { return true; } + + protected: + explicit ErrorInstance(JSGlobalData*, NonNullPassRefPtr<Structure>); + explicit ErrorInstance(JSGlobalData*, NonNullPassRefPtr<Structure>, const UString&); + + bool m_appendSourceToMessage; + }; + +} // namespace JSC + +#endif // ErrorInstance_h diff --git a/Source/JavaScriptCore/runtime/ErrorPrototype.cpp b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp new file mode 100644 index 0000000..d18e7d8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ErrorPrototype.h" + +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "ObjectPrototype.h" +#include "PrototypeFunction.h" +#include "UString.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); + +static EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*); + +// ECMA 15.9.4 +ErrorPrototype::ErrorPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) + : ErrorInstance(&exec->globalData(), structure) +{ + // The constructor will be added later in ErrorConstructor's constructor + + putDirectWithoutTransition(exec->propertyNames().name, jsNontrivialString(exec, "Error"), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, errorProtoFuncToString), DontEnum); +} + +EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); + JSValue name = thisObj->get(exec, exec->propertyNames().name); + JSValue message = thisObj->get(exec, exec->propertyNames().message); + + // Mozilla-compatible format. + + if (!name.isUndefined()) { + if (!message.isUndefined()) + return JSValue::encode(jsMakeNontrivialString(exec, name.toString(exec), ": ", message.toString(exec))); + return JSValue::encode(jsNontrivialString(exec, name.toString(exec))); + } + if (!message.isUndefined()) + return JSValue::encode(jsMakeNontrivialString(exec, "Error: ", message.toString(exec))); + return JSValue::encode(jsNontrivialString(exec, "Error")); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorPrototype.h b/Source/JavaScriptCore/runtime/ErrorPrototype.h new file mode 100644 index 0000000..fce2742 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ErrorPrototype_h +#define ErrorPrototype_h + +#include "ErrorInstance.h" + +namespace JSC { + + class ObjectPrototype; + + class ErrorPrototype : public ErrorInstance { + public: + ErrorPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + }; + +} // namespace JSC + +#endif // ErrorPrototype_h diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp new file mode 100644 index 0000000..1ef264c --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -0,0 +1,145 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "ExceptionHelpers.h" + +#include "CodeBlock.h" +#include "CallFrame.h" +#include "ErrorInstance.h" +#include "JSGlobalObjectFunctions.h" +#include "JSObject.h" +#include "JSNotAnObject.h" +#include "Interpreter.h" +#include "Nodes.h" +#include "UStringConcatenate.h" + +namespace JSC { + +class InterruptedExecutionError : public JSObject { +public: + InterruptedExecutionError(JSGlobalData* globalData) + : JSObject(globalData->interruptedExecutionErrorStructure) + { + } + + virtual ComplType exceptionType() const { return Interrupted; } + + virtual UString toString(ExecState*) const { return "JavaScript execution exceeded timeout."; } +}; + +JSObject* createInterruptedExecutionException(JSGlobalData* globalData) +{ + return new (globalData) InterruptedExecutionError(globalData); +} + +class TerminatedExecutionError : public JSObject { +public: + TerminatedExecutionError(JSGlobalData* globalData) + : JSObject(globalData->terminatedExecutionErrorStructure) + { + } + + virtual ComplType exceptionType() const { return Terminated; } + + virtual UString toString(ExecState*) const { return "JavaScript execution terminated."; } +}; + +JSObject* createTerminatedExecutionException(JSGlobalData* globalData) +{ + return new (globalData) TerminatedExecutionError(globalData); +} + +JSObject* createStackOverflowError(ExecState* exec) +{ + return createRangeError(exec, "Maximum call stack size exceeded."); +} + +JSObject* createStackOverflowError(JSGlobalObject* globalObject) +{ + return createRangeError(globalObject, "Maximum call stack size exceeded."); +} + +JSObject* createUndefinedVariableError(ExecState* exec, const Identifier& ident) +{ + UString message(makeUString("Can't find variable: ", ident.ustring())); + return createReferenceError(exec, message); +} + +JSObject* createInvalidParamError(ExecState* exec, const char* op, JSValue value) +{ + UString errorMessage = makeUString("'", value.toString(exec), "' is not a valid argument for '", op, "'"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); + return exception; +} + +JSObject* createNotAConstructorError(ExecState* exec, JSValue value) +{ + UString errorMessage = makeUString("'", value.toString(exec), "' is not a constructor"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); + return exception; +} + +JSObject* createNotAFunctionError(ExecState* exec, JSValue value) +{ + UString errorMessage = makeUString("'", value.toString(exec), "' is not a function"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); + return exception; +} + +JSObject* createNotAnObjectError(ExecState* exec, JSValue value) +{ + UString errorMessage = makeUString("'", value.toString(exec), "' is not an object"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); + return exception; +} + +JSObject* createErrorForInvalidGlobalAssignment(ExecState* exec, const UString& propertyName) +{ + return createReferenceError(exec, makeUString("Strict mode forbids implicit creation of global property '", propertyName, "'")); +} + +JSObject* throwOutOfMemoryError(ExecState* exec) +{ + return throwError(exec, createError(exec, "Out of memory")); +} + +JSObject* throwStackOverflowError(ExecState* exec) +{ + return throwError(exec, createStackOverflowError(exec)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.h b/Source/JavaScriptCore/runtime/ExceptionHelpers.h new file mode 100644 index 0000000..7edffad --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 ExceptionHelpers_h +#define ExceptionHelpers_h + +#include "JSValue.h" + +namespace JSC { + + class CodeBlock; + class ExecState; + class Identifier; + class JSGlobalData; + class JSGlobalObject; + class JSNotAnObjectErrorStub; + class JSObject; + class Node; + struct Instruction; + + JSObject* createInterruptedExecutionException(JSGlobalData*); + JSObject* createTerminatedExecutionException(JSGlobalData*); + JSObject* createStackOverflowError(ExecState*); + JSObject* createStackOverflowError(JSGlobalObject*); + JSObject* createUndefinedVariableError(ExecState*, const Identifier&); + JSObject* createNotAnObjectError(ExecState*, JSValue); + JSObject* createInvalidParamError(ExecState*, const char* op, JSValue); + JSObject* createNotAConstructorError(ExecState*, JSValue); + JSObject* createNotAFunctionError(ExecState*, JSValue); + JSObject* createErrorForInvalidGlobalAssignment(ExecState*, const UString&); + + JSObject* throwOutOfMemoryError(ExecState*); + JSObject* throwStackOverflowError(ExecState*); + +} // namespace JSC + +#endif // ExceptionHelpers_h diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp new file mode 100644 index 0000000..c7262be --- /dev/null +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2009, 2010 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 "UStringBuilder.h" +#include "Vector.h" + +namespace JSC { + +#if ENABLE(JIT) +NativeExecutable::~NativeExecutable() +{ +} +#endif + +VPtrHackExecutable::~VPtrHackExecutable() +{ +} + +EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext) + : ScriptExecutable(exec, source, inStrictContext) +{ +} + +EvalExecutable::~EvalExecutable() +{ +} + +ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source) + : ScriptExecutable(exec, source, false) +{ +} + +ProgramExecutable::~ProgramExecutable() +{ +} + +FunctionExecutable::FunctionExecutable(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext, int firstLine, int lastLine) + : ScriptExecutable(globalData, source, inStrictContext) + , m_numCapturedVariables(0) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_name(name) + , m_symbolTable(0) +{ + m_firstLine = firstLine; + m_lastLine = lastLine; +} + +FunctionExecutable::FunctionExecutable(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext, int firstLine, int lastLine) + : ScriptExecutable(exec, source, inStrictContext) + , m_numCapturedVariables(0) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_name(name) + , m_symbolTable(0) +{ + m_firstLine = firstLine; + m_lastLine = lastLine; +} + +FunctionExecutable::~FunctionExecutable() +{ +} + +JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<EvalNode> evalNode = globalData->parser->parse<EvalNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!evalNode) { + ASSERT(exception); + return exception; + } + recordParse(evalNode->features(), evalNode->hasCapturedVariables(), evalNode->lineNo(), evalNode->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + ASSERT(!m_evalCodeBlock); + m_evalCodeBlock = adoptPtr(new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth())); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(evalNode.get(), scopeChain, m_evalCodeBlock->symbolTable(), m_evalCodeBlock.get()))); + generator->generate(); + + evalNode->destroyData(); + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_evalCodeBlock.get()); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_evalCodeBlock->discardBytecode(); +#endif + } +#endif + + return 0; +} + +JSObject* ProgramExecutable::checkSyntax(ExecState* exec) +{ + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> programNode = globalData->parser->parse<ProgramNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, JSParseNormal, &exception); + if (programNode) + return 0; + ASSERT(exception); + return exception; +} + +JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + ASSERT(!m_programCodeBlock); + + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> programNode = globalData->parser->parse<ProgramNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!programNode) { + ASSERT(exception); + return exception; + } + recordParse(programNode->features(), programNode->hasCapturedVariables(), programNode->lineNo(), programNode->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + m_programCodeBlock = adoptPtr(new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider())); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(programNode.get(), scopeChain, &globalObject->symbolTable(), m_programCodeBlock.get()))); + generator->generate(); + + programNode->destroyData(); + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_programCodeBlock.get()); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_programCodeBlock->discardBytecode(); +#endif + } +#endif + + return 0; +} + +JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + JSObject* exception = 0; + JSGlobalData* globalData = scopeChainNode->globalData; + RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!body) { + ASSERT(exception); + return exception; + } + if (m_forceUsesArguments) + body->setUsesArguments(); + body->finishParsing(m_parameters, m_name); + recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + ASSERT(!m_codeBlockForCall); + m_codeBlockForCall = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), false)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChain, m_codeBlockForCall->symbolTable(), m_codeBlockForCall.get()))); + generator->generate(); + m_numParametersForCall = m_codeBlockForCall->m_numParameters; + ASSERT(m_numParametersForCall); + m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars; + m_symbolTable = m_codeBlockForCall->sharedSymbolTable(); + + body->destroyData(); + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_codeBlockForCall.get(), &m_jitCodeForCallWithArityCheck); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_codeBlockForCall->discardBytecode(); +#endif + } +#endif + + return 0; +} + +JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + JSObject* exception = 0; + JSGlobalData* globalData = scopeChainNode->globalData; + RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!body) { + ASSERT(exception); + return exception; + } + if (m_forceUsesArguments) + body->setUsesArguments(); + body->finishParsing(m_parameters, m_name); + recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); + + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + ASSERT(!m_codeBlockForConstruct); + m_codeBlockForConstruct = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), true)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChain, m_codeBlockForConstruct->symbolTable(), m_codeBlockForConstruct.get()))); + generator->generate(); + m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters; + ASSERT(m_numParametersForConstruct); + m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars; + m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable(); + + body->destroyData(); + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + m_jitCodeForConstruct = JIT::compile(scopeChainNode->globalData, m_codeBlockForConstruct.get(), &m_jitCodeForConstructWithArityCheck); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_codeBlockForConstruct->discardBytecode(); +#endif + } +#endif + + return 0; +} + +void FunctionExecutable::markAggregate(MarkStack& markStack) +{ + if (m_codeBlockForCall) + m_codeBlockForCall->markAggregate(markStack); + if (m_codeBlockForConstruct) + m_codeBlockForConstruct->markAggregate(markStack); +} + +void FunctionExecutable::discardCode() +{ + m_codeBlockForCall.clear(); + m_codeBlockForConstruct.clear(); + m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED; + m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED; +#if ENABLE(JIT) + m_jitCodeForCall = JITCode(); + m_jitCodeForConstruct = JITCode(); +#endif +} + +PassRefPtr<FunctionExecutable> FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception) +{ + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> program = exec->globalData().parser->parse<ProgramNode>(lexicalGlobalObject, debugger, exec, source, 0, JSParseNormal, exception); + if (!program) { + ASSERT(*exception); + return 0; + } + + // Uses of this function that would not result in a single function expression are invalid. + StatementNode* exprStatement = program->singleStatement(); + ASSERT(exprStatement); + ASSERT(exprStatement->isExprStatement()); + ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr(); + ASSERT(funcExpr); + ASSERT(funcExpr->isFuncExprNode()); + FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body(); + ASSERT(body); + + return FunctionExecutable::create(&exec->globalData(), functionName, body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine()); +} + +UString FunctionExecutable::paramString() const +{ + FunctionParameters& parameters = *m_parameters; + UStringBuilder builder; + for (size_t pos = 0; pos < parameters.size(); ++pos) { + if (!builder.isEmpty()) + builder.append(", "); + builder.append(parameters[pos].ustring()); + } + return builder.toUString(); +} + +} diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h new file mode 100644 index 0000000..544e487 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2009, 2010 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 "CallData.h" +#include "JSFunction.h" +#include "Interpreter.h" +#include "Nodes.h" +#include "SamplingTool.h" +#include <wtf/PassOwnPtr.h> + +namespace JSC { + + class CodeBlock; + class Debugger; + class EvalCodeBlock; + class FunctionCodeBlock; + 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_numParametersForCall(numParameters) + , m_numParametersForConstruct(numParameters) + { + } + + virtual ~ExecutableBase() {} + + bool isHostFunction() const + { + ASSERT((m_numParametersForCall == NUM_PARAMETERS_IS_HOST) == (m_numParametersForConstruct == NUM_PARAMETERS_IS_HOST)); + return m_numParametersForCall == NUM_PARAMETERS_IS_HOST; + } + + protected: + int m_numParametersForCall; + int m_numParametersForConstruct; + +#if ENABLE(JIT) + public: + JITCode& generatedJITCodeForCall() + { + ASSERT(m_jitCodeForCall); + return m_jitCodeForCall; + } + + JITCode& generatedJITCodeForConstruct() + { + ASSERT(m_jitCodeForConstruct); + return m_jitCodeForConstruct; + } + + protected: + JITCode m_jitCodeForCall; + JITCode m_jitCodeForConstruct; + MacroAssemblerCodePtr m_jitCodeForCallWithArityCheck; + MacroAssemblerCodePtr m_jitCodeForConstructWithArityCheck; +#endif + }; + +#if ENABLE(JIT) + class NativeExecutable : public ExecutableBase { + friend class JIT; + public: + static PassRefPtr<NativeExecutable> create(MacroAssemblerCodePtr callThunk, NativeFunction function, MacroAssemblerCodePtr constructThunk, NativeFunction constructor) + { + if (!callThunk) + return adoptRef(new NativeExecutable(JITCode(), function, JITCode(), constructor)); + return adoptRef(new NativeExecutable(JITCode::HostFunction(callThunk), function, JITCode::HostFunction(constructThunk), constructor)); + } + + ~NativeExecutable(); + + NativeFunction function() { return m_function; } + + private: + NativeExecutable(JITCode callThunk, NativeFunction function, JITCode constructThunk, NativeFunction constructor) + : ExecutableBase(NUM_PARAMETERS_IS_HOST) + , m_function(function) + , m_constructor(constructor) + { + m_jitCodeForCall = callThunk; + m_jitCodeForConstruct = constructThunk; + m_jitCodeForCallWithArityCheck = callThunk.addressForCall(); + m_jitCodeForConstructWithArityCheck = constructThunk.addressForCall(); + } + + NativeFunction m_function; + // Probably should be a NativeConstructor, but this will currently require rewriting the JIT + // trampoline. It may be easier to make NativeFunction be passed 'this' as a part of the ArgList. + NativeFunction m_constructor; + }; +#endif + + class VPtrHackExecutable : public ExecutableBase { + public: + VPtrHackExecutable() + : ExecutableBase(NUM_PARAMETERS_IS_HOST) + { + } + + ~VPtrHackExecutable(); + }; + + class ScriptExecutable : public ExecutableBase { + public: + ScriptExecutable(JSGlobalData* globalData, const SourceCode& source, bool isInStrictContext) + : ExecutableBase(NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(isInStrictContext ? StrictModeFeature : 0) + { +#if ENABLE(CODEBLOCK_SAMPLING) + relaxAdoptionRequirement(); + if (SamplingTool* sampler = globalData->interpreter->sampler()) + sampler->notifyOfScope(this); +#else + UNUSED_PARAM(globalData); +#endif + } + + ScriptExecutable(ExecState* exec, const SourceCode& source, bool isInStrictContext) + : ExecutableBase(NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(isInStrictContext ? StrictModeFeature : 0) + { +#if ENABLE(CODEBLOCK_SAMPLING) + relaxAdoptionRequirement(); + 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_hasCapturedVariables || m_features & (EvalFeature | WithFeature | CatchFeature); } + bool isStrictMode() const { return m_features & StrictModeFeature; } + + protected: + void recordParse(CodeFeatures features, bool hasCapturedVariables, int firstLine, int lastLine) + { + m_features = features; + m_hasCapturedVariables = hasCapturedVariables; + m_firstLine = firstLine; + m_lastLine = lastLine; + } + + SourceCode m_source; + CodeFeatures m_features; + bool m_hasCapturedVariables; + int m_firstLine; + int m_lastLine; + }; + + class EvalExecutable : public ScriptExecutable { + public: + + ~EvalExecutable(); + + JSObject* compile(ExecState* exec, ScopeChainNode* scopeChainNode) + { + JSObject* error = 0; + if (!m_evalCodeBlock) + error = compileInternal(exec, scopeChainNode); + ASSERT(!error == !!m_evalCodeBlock); + return error; + } + + EvalCodeBlock& generatedBytecode() + { + ASSERT(m_evalCodeBlock); + return *m_evalCodeBlock; + } + + static PassRefPtr<EvalExecutable> create(ExecState* exec, const SourceCode& source, bool isInStrictContext) { return adoptRef(new EvalExecutable(exec, source, isInStrictContext)); } + +#if ENABLE(JIT) + JITCode& generatedJITCode() + { + return generatedJITCodeForCall(); + } +#endif + + private: + EvalExecutable(ExecState*, const SourceCode&, bool); + + JSObject* compileInternal(ExecState*, ScopeChainNode*); + + OwnPtr<EvalCodeBlock> m_evalCodeBlock; + }; + + class ProgramExecutable : public ScriptExecutable { + public: + static PassRefPtr<ProgramExecutable> create(ExecState* exec, const SourceCode& source) + { + return adoptRef(new ProgramExecutable(exec, source)); + } + + ~ProgramExecutable(); + + JSObject* compile(ExecState* exec, ScopeChainNode* scopeChainNode) + { + JSObject* error = 0; + if (!m_programCodeBlock) + error = compileInternal(exec, scopeChainNode); + ASSERT(!error == !!m_programCodeBlock); + return error; + } + + ProgramCodeBlock& generatedBytecode() + { + ASSERT(m_programCodeBlock); + return *m_programCodeBlock; + } + + JSObject* checkSyntax(ExecState*); + +#if ENABLE(JIT) + JITCode& generatedJITCode() + { + return generatedJITCodeForCall(); + } +#endif + + private: + ProgramExecutable(ExecState*, const SourceCode&); + + JSObject* compileInternal(ExecState*, ScopeChainNode*); + + OwnPtr<ProgramCodeBlock> m_programCodeBlock; + }; + + class FunctionExecutable : public ScriptExecutable { + friend class JIT; + public: + static PassRefPtr<FunctionExecutable> create(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool isInStrictContext, int firstLine, int lastLine) + { + return adoptRef(new FunctionExecutable(exec, name, source, forceUsesArguments, parameters, isInStrictContext, firstLine, lastLine)); + } + + static PassRefPtr<FunctionExecutable> create(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool isInStrictContext, int firstLine, int lastLine) + { + return adoptRef(new FunctionExecutable(globalData, name, source, forceUsesArguments, parameters, isInStrictContext, firstLine, lastLine)); + } + + ~FunctionExecutable(); + + JSFunction* make(ExecState* exec, ScopeChainNode* scopeChain) + { + return new (exec) JSFunction(exec, this, scopeChain); + } + + // Returns either call or construct bytecode. This can be appropriate + // for answering questions that that don't vary between call and construct -- + // for example, argumentsRegister(). + FunctionCodeBlock& generatedBytecode() + { + if (m_codeBlockForCall) + return *m_codeBlockForCall; + ASSERT(m_codeBlockForConstruct); + return *m_codeBlockForConstruct; + } + + JSObject* compileForCall(ExecState* exec, ScopeChainNode* scopeChainNode) + { + JSObject* error = 0; + if (!m_codeBlockForCall) + error = compileForCallInternal(exec, scopeChainNode); + ASSERT(!error == !!m_codeBlockForCall); + return error; + } + + bool isGeneratedForCall() const + { + return m_codeBlockForCall; + } + + FunctionCodeBlock& generatedBytecodeForCall() + { + ASSERT(m_codeBlockForCall); + return *m_codeBlockForCall; + } + + JSObject* compileForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode) + { + JSObject* error = 0; + if (!m_codeBlockForConstruct) + error = compileForConstructInternal(exec, scopeChainNode); + ASSERT(!error == !!m_codeBlockForConstruct); + return error; + } + + bool isGeneratedForConstruct() const + { + return m_codeBlockForConstruct; + } + + FunctionCodeBlock& generatedBytecodeForConstruct() + { + ASSERT(m_codeBlockForConstruct); + return *m_codeBlockForConstruct; + } + + const Identifier& name() { return m_name; } + size_t parameterCount() const { return m_parameters->size(); } + unsigned capturedVariableCount() const { return m_numCapturedVariables; } + UString paramString() const; + SharedSymbolTable* symbolTable() const { return m_symbolTable; } + + void discardCode(); + void markAggregate(MarkStack&); + static PassRefPtr<FunctionExecutable> fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, JSObject** exception); + + private: + FunctionExecutable(JSGlobalData*, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool, int firstLine, int lastLine); + FunctionExecutable(ExecState*, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool, int firstLine, int lastLine); + + JSObject* compileForCallInternal(ExecState*, ScopeChainNode*); + JSObject* compileForConstructInternal(ExecState*, ScopeChainNode*); + + unsigned m_numCapturedVariables : 31; + bool m_forceUsesArguments : 1; + + RefPtr<FunctionParameters> m_parameters; + OwnPtr<FunctionCodeBlock> m_codeBlockForCall; + OwnPtr<FunctionCodeBlock> m_codeBlockForConstruct; + Identifier m_name; + SharedSymbolTable* m_symbolTable; + +#if ENABLE(JIT) + public: + MacroAssemblerCodePtr generatedJITCodeForCallWithArityCheck() + { + ASSERT(m_jitCodeForCall); + ASSERT(m_jitCodeForCallWithArityCheck); + return m_jitCodeForCallWithArityCheck; + } + + MacroAssemblerCodePtr generatedJITCodeForConstructWithArityCheck() + { + ASSERT(m_jitCodeForConstruct); + ASSERT(m_jitCodeForConstructWithArityCheck); + return m_jitCodeForConstructWithArityCheck; + } +#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(); + } + +#if ENABLE(JIT) + inline NativeFunction JSFunction::nativeFunction() + { + ASSERT(isHostFunction()); + return static_cast<NativeExecutable*>(m_executable.get())->function(); + } +#endif +} + +#endif diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp new file mode 100644 index 0000000..45b4802 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "FunctionConstructor.h" + +#include "Debugger.h" +#include "ExceptionHelpers.h" +#include "FunctionPrototype.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Lexer.h" +#include "Nodes.h" +#include "Parser.h" +#include "UStringBuilder.h" +#include "UStringConcatenate.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(FunctionConstructor); + +FunctionConstructor::FunctionConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, FunctionPrototype* functionPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, functionPrototype->classInfo()->className)) +{ + putDirectWithoutTransition(exec->propertyNames().prototype, functionPrototype, DontEnum | DontDelete | ReadOnly); + + // Number of arguments for constructor + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +static EncodedJSValue JSC_HOST_CALL constructWithFunctionConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructFunction(exec, args)); +} + +ConstructType FunctionConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithFunctionConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callFunctionConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructFunction(exec, args)); +} + +// ECMA 15.3.1 The Function Constructor Called as a Function +CallType FunctionConstructor::getCallData(CallData& callData) +{ + callData.native.function = callFunctionConstructor; + return CallTypeHost; +} + +// ECMA 15.3.2 The Function Constructor +JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifier& functionName, const UString& sourceURL, int lineNumber) +{ + // Functions need to have a space following the opening { due to for web compatibility + // see https://bugs.webkit.org/show_bug.cgi?id=24350 + // We also need \n before the closing } to handle // comments at the end of the last line + UString program; + if (args.isEmpty()) + program = "(function() { \n})"; + else if (args.size() == 1) + program = makeUString("(function() { ", args.at(0).toString(exec), "\n})"); + else { + UStringBuilder builder; + builder.append("(function("); + builder.append(args.at(0).toString(exec)); + for (size_t i = 1; i < args.size() - 1; i++) { + builder.append(","); + builder.append(args.at(i).toString(exec)); + } + builder.append(") { "); + builder.append(args.at(args.size() - 1).toString(exec)); + builder.append("\n})"); + program = builder.toUString(); + } + + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + JSGlobalData& globalData = globalObject->globalData(); + SourceCode source = makeSource(program, sourceURL, lineNumber); + JSObject* exception = 0; + RefPtr<FunctionExecutable> function = FunctionExecutable::fromGlobalCode(functionName, exec, exec->dynamicGlobalObject()->debugger(), source, &exception); + if (!function) { + ASSERT(exception); + return throwError(exec, exception); + } + + ScopeChain scopeChain(globalObject, &globalData, globalObject, exec->globalThisValue()); + return new (exec) JSFunction(exec, function, scopeChain.node()); +} + +// ECMA 15.3.2 The Function Constructor +JSObject* constructFunction(ExecState* exec, const ArgList& args) +{ + return constructFunction(exec, args, Identifier(exec, "anonymous"), UString(), 1); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.h b/Source/JavaScriptCore/runtime/FunctionConstructor.h new file mode 100644 index 0000000..6af4861 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FunctionConstructor_h +#define FunctionConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class FunctionPrototype; + + class FunctionConstructor : public InternalFunction { + public: + FunctionConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, FunctionPrototype*); + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + + JSObject* constructFunction(ExecState*, const ArgList&, const Identifier& functionName, const UString& sourceURL, int lineNumber); + JSObject* constructFunction(ExecState*, const ArgList&); + +} // namespace JSC + +#endif // FunctionConstructor_h diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp new file mode 100644 index 0000000..cd7739d --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "FunctionPrototype.h" + +#include "Arguments.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Interpreter.h" +#include "Lexer.h" +#include "PrototypeFunction.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(FunctionPrototype); + +static EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState*); + +FunctionPrototype::FunctionPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : InternalFunction(&exec->globalData(), globalObject, structure, exec->propertyNames().nullIdentifier) +{ + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction) +{ + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, functionProtoFuncToString), DontEnum); + *applyFunction = new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().apply, functionProtoFuncApply); + putDirectFunctionWithoutTransition(exec, *applyFunction, DontEnum); + *callFunction = new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().call, functionProtoFuncCall); + putDirectFunctionWithoutTransition(exec, *callFunction, DontEnum); +} + +static EncodedJSValue JSC_HOST_CALL callFunctionPrototype(ExecState*) +{ + return JSValue::encode(jsUndefined()); +} + +// ECMA 15.3.4 +CallType FunctionPrototype::getCallData(CallData& callData) +{ + callData.native.function = callFunctionPrototype; + return CallTypeHost; +} + +// Functions + +// Compatibility hack for the Optimost JavaScript library. (See <rdar://problem/6595040>.) +static inline void insertSemicolonIfNeeded(UString& functionBody) +{ + ASSERT(functionBody[0] == '{'); + ASSERT(functionBody[functionBody.length() - 1] == '}'); + + for (size_t i = functionBody.length() - 2; i > 0; --i) { + UChar ch = functionBody[i]; + if (!Lexer::isWhiteSpace(ch) && !Lexer::isLineTerminator(ch)) { + if (ch != ';' && ch != '}') + functionBody = makeUString(functionBody.substringSharingImpl(0, i + 1), ";", functionBody.substringSharingImpl(i + 1, functionBody.length() - (i + 1))); + return; + } + } +} + +EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.inherits(&JSFunction::info)) { + JSFunction* function = asFunction(thisValue); + if (function->isHostFunction()) + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); + FunctionExecutable* executable = function->jsExecutable(); + UString sourceString = executable->source().toString(); + insertSemicolonIfNeeded(sourceString); + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "(", executable->paramString(), ") ", sourceString)); + } + + if (thisValue.inherits(&InternalFunction::info)) { + InternalFunction* function = asInternalFunction(thisValue); + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); + } + + return throwVMTypeError(exec); +} + +EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + CallData callData; + CallType callType = getCallData(thisValue, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSValue array = exec->argument(1); + + MarkedArgumentBuffer applyArgs; + if (!array.isUndefinedOrNull()) { + if (!array.isObject()) + return throwVMTypeError(exec); + if (asObject(array)->classInfo() == &Arguments::info) + asArguments(array)->fillArgList(exec, applyArgs); + else if (isJSArray(&exec->globalData(), array)) + asArray(array)->fillArgList(exec, applyArgs); + else if (asObject(array)->inherits(&JSArray::info)) { + unsigned length = asArray(array)->get(exec, exec->propertyNames().length).toUInt32(exec); + for (unsigned i = 0; i < length; ++i) + applyArgs.append(asArray(array)->get(exec, i)); + } else + return throwVMTypeError(exec); + } + + return JSValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), applyArgs)); +} + +EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + CallData callData; + CallType callType = getCallData(thisValue, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + ArgList args(exec); + ArgList callArgs; + args.getSlice(1, callArgs); + return JSValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), callArgs)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.h b/Source/JavaScriptCore/runtime/FunctionPrototype.h new file mode 100644 index 0000000..5661194 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FunctionPrototype_h +#define FunctionPrototype_h + +#include "InternalFunction.h" + +namespace JSC { + + class PrototypeFunction; + + class FunctionPrototype : public InternalFunction { + public: + FunctionPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>); + void addFunctionProperties(ExecState*, JSGlobalObject*, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction); + + static PassRefPtr<Structure> createStructure(JSValue proto) + { + return Structure::create(proto, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + private: + virtual CallType getCallData(CallData&); + }; + +} // namespace JSC + +#endif // FunctionPrototype_h diff --git a/Source/JavaScriptCore/runtime/GCActivityCallback.cpp b/Source/JavaScriptCore/runtime/GCActivityCallback.cpp new file mode 100644 index 0000000..161abfb --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCActivityCallback.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "GCActivityCallback.h" + +namespace JSC { + +struct DefaultGCActivityCallbackPlatformData { +}; + +DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap) +{ +} + +DefaultGCActivityCallback::~DefaultGCActivityCallback() +{ +} + +void DefaultGCActivityCallback::operator()() +{ +} + +void DefaultGCActivityCallback::synchronize() +{ +} + +} + diff --git a/Source/JavaScriptCore/runtime/GCActivityCallback.h b/Source/JavaScriptCore/runtime/GCActivityCallback.h new file mode 100644 index 0000000..862b4df --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCActivityCallback.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 GCActivityCallback_h +#define GCActivityCallback_h + +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +class Heap; + +class GCActivityCallback { +public: + virtual ~GCActivityCallback() {} + virtual void operator()() {} + virtual void synchronize() {} + +protected: + GCActivityCallback() {} +}; + +struct DefaultGCActivityCallbackPlatformData; + +class DefaultGCActivityCallback : public GCActivityCallback { +public: + static PassOwnPtr<DefaultGCActivityCallback> create(Heap*); + + DefaultGCActivityCallback(Heap*); + ~DefaultGCActivityCallback(); + + void operator()(); + void synchronize(); + +private: + OwnPtr<DefaultGCActivityCallbackPlatformData*> d; +}; + +inline PassOwnPtr<DefaultGCActivityCallback> DefaultGCActivityCallback::create(Heap* heap) +{ + return adoptPtr(new DefaultGCActivityCallback(heap)); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp new file mode 100644 index 0000000..7168a05 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "GCActivityCallback.h" + +#include "APIShims.h" +#include "Collector.h" +#include "JSGlobalData.h" +#include "JSLock.h" +#include <wtf/RetainPtr.h> +#include <wtf/WTFThreadData.h> +#include <CoreFoundation/CoreFoundation.h> + +#if !PLATFORM(CF) +#error "This file should only be used on CF platforms." +#endif + +namespace JSC { + +struct DefaultGCActivityCallbackPlatformData { + static void trigger(CFRunLoopTimerRef, void *info); + + RetainPtr<CFRunLoopTimerRef> timer; + RetainPtr<CFRunLoopRef> runLoop; + CFRunLoopTimerContext context; +}; + +const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10; +const CFTimeInterval triggerInterval = 2; // seconds + +void DefaultGCActivityCallbackPlatformData::trigger(CFRunLoopTimerRef, void *info) +{ + Heap* heap = static_cast<Heap*>(info); + APIEntryShim shim(heap->globalData()); + heap->collectAllGarbage(); +} + +DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap) +{ + d = adoptPtr(new DefaultGCActivityCallbackPlatformData); + + memset(&d->context, '\0', sizeof(CFRunLoopTimerContext)); + d->context.info = heap; + d->runLoop = CFRunLoopGetCurrent(); + d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::trigger, &d->context)); + CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); +} + +DefaultGCActivityCallback::~DefaultGCActivityCallback() +{ + CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); + CFRunLoopTimerInvalidate(d->timer.get()); + d->context.info = 0; + d->runLoop = 0; + d->timer = 0; +} + +void DefaultGCActivityCallback::operator()() +{ + CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + triggerInterval); +} + +void DefaultGCActivityCallback::synchronize() +{ + if (CFRunLoopGetCurrent() == d->runLoop.get()) + return; + CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); + d->runLoop = CFRunLoopGetCurrent(); + CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); +} + +} diff --git a/Source/JavaScriptCore/runtime/GCHandle.cpp b/Source/JavaScriptCore/runtime/GCHandle.cpp new file mode 100644 index 0000000..297de38 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCHandle.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "GCHandle.h" + +namespace JSC { + +WeakGCHandlePool* WeakGCHandle::pool() +{ + uintptr_t pool = (reinterpret_cast<uintptr_t>(this) & WeakGCHandlePool::poolMask); + return reinterpret_cast<WeakGCHandlePool*>(pool); +} + +WeakGCHandlePool::WeakGCHandlePool() +{ + ASSERT(sizeof(WeakGCHandlePool) <= WeakGCHandlePool::poolSize); + m_entriesSize = 0; + m_initialAlloc = 1; + m_entries[0].setNextInFreeList(0); +} + +WeakGCHandle* WeakGCHandlePool::allocate(JSCell* cell) +{ + ASSERT(cell); + ASSERT(m_entries[0].isNext()); + unsigned freeList = m_entries[0].getNextInFreeList(); + ASSERT(freeList < WeakGCHandlePool::numPoolEntries); + ASSERT(m_entriesSize < WeakGCHandlePool::numPoolEntries); + + if (m_entriesSize == WeakGCHandlePool::numPoolEntries - 1) + return 0; + + if (freeList) { + unsigned i = freeList; + freeList = m_entries[i].getNextInFreeList(); + m_entries[i].set(cell); + m_entries[0].setNextInFreeList(freeList); + ++m_entriesSize; + return &m_entries[i]; + } + + ASSERT(m_initialAlloc < WeakGCHandlePool::numPoolEntries); + + unsigned i = m_initialAlloc; + ++m_initialAlloc; + m_entries[i].set(cell); + ++m_entriesSize; + return &m_entries[i]; + +} + +void WeakGCHandlePool::free(WeakGCHandle* handle) +{ + ASSERT(handle->pool() == this); + ASSERT(m_entries[0].isNext()); + unsigned freeList = m_entries[0].getNextInFreeList(); + ASSERT(freeList < WeakGCHandlePool::numPoolEntries); + handle->setNextInFreeList(freeList); + m_entries[0].setNextInFreeList(handle - m_entries); + --m_entriesSize; +} + +} diff --git a/Source/JavaScriptCore/runtime/GCHandle.h b/Source/JavaScriptCore/runtime/GCHandle.h new file mode 100644 index 0000000..8818f79 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCHandle.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 GCHandle_h +#define GCHandle_h + +#include <wtf/Assertions.h> + +namespace JSC { + +class Heap; +class JSCell; +class WeakGCHandle; +class WeakGCHandlePool; + +class WeakGCHandle { + friend class WeakGCHandlePool; + +public: + // Because JSCell objects are aligned, we can use the lower two bits as + // status flags. The least significant bit is set when the handle is not a + // pointer, i.e. when it's used as a offset for the free list in + // WeakGCHandlePool. The second least significant bit is set when the object + // the pointer corresponds to has been deleted by a garbage collection + + bool isValidPtr() { return !(m_ptr & 3); } + bool isPtr() { return !(m_ptr & 1); } + bool isNext() { return (m_ptr & 3) == 1; } + + void invalidate() + { + ASSERT(isValidPtr()); + m_ptr |= 2; + } + + JSCell* get() + { + ASSERT(isPtr()); + return reinterpret_cast<JSCell*>(m_ptr & ~3); + } + + void set(JSCell* p) + { + m_ptr = reinterpret_cast<uintptr_t>(p); + ASSERT(isPtr()); + } + + WeakGCHandlePool* pool(); + +private: + uintptr_t getNextInFreeList() + { + ASSERT(isNext()); + return m_ptr >> 2; + } + + void setNextInFreeList(uintptr_t n) + { + m_ptr = (n << 2) | 1; + ASSERT(isNext()); + } + + uintptr_t m_ptr; +}; + +class WeakGCHandlePool { +public: + static const size_t poolSize = 32 * 1024; // 32k + static const size_t poolMask = ~(poolSize - 1); + static const size_t numPoolEntries = (poolSize - sizeof(Heap*) - 3 * sizeof(unsigned)) / sizeof(WeakGCHandle); + + WeakGCHandlePool(); + + WeakGCHandle* allocate(JSCell* cell); + void free(WeakGCHandle*); + + bool isFull() + { + ASSERT(m_entriesSize < WeakGCHandlePool::numPoolEntries); + return m_entriesSize == WeakGCHandlePool::numPoolEntries - 1; + } + + void update(); + +private: + Heap* m_heap; + unsigned m_entriesSize; + unsigned m_initialAlloc; + + WeakGCHandle m_entries[WeakGCHandlePool::numPoolEntries]; +}; + +} +#endif diff --git a/Source/JavaScriptCore/runtime/GetterSetter.cpp b/Source/JavaScriptCore/runtime/GetterSetter.cpp new file mode 100644 index 0000000..7e54053 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GetterSetter.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 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 + * 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 "GetterSetter.h" + +#include "JSObject.h" +#include <wtf/Assertions.h> + +namespace JSC { + +void GetterSetter::markChildren(MarkStack& markStack) +{ + JSCell::markChildren(markStack); + + if (m_getter) + markStack.append(m_getter); + if (m_setter) + markStack.append(m_setter); +} + +bool GetterSetter::isGetterSetter() const +{ + return true; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/GetterSetter.h b/Source/JavaScriptCore/runtime/GetterSetter.h new file mode 100644 index 0000000..e7b1938 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GetterSetter.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef GetterSetter_h +#define GetterSetter_h + +#include "JSCell.h" + +#include "CallFrame.h" + +namespace JSC { + + class JSObject; + + // This is an internal value object which stores getter and setter functions + // for a property. + class GetterSetter : public JSCell { + friend class JIT; + public: + GetterSetter(ExecState* exec) + : JSCell(exec->globalData().getterSetterStructure.get()) + , m_getter(0) + , m_setter(0) + { + } + + virtual void markChildren(MarkStack&); + + JSObject* getter() const { return m_getter; } + void setGetter(JSObject* getter) { m_getter = getter; } + JSObject* setter() const { return m_setter; } + void setSetter(JSObject* setter) { m_setter = setter; } + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(GetterSetterType, OverridesMarkChildren), AnonymousSlotCount); + } + private: + virtual bool isGetterSetter() const; + + JSObject* m_getter; + JSObject* m_setter; + }; + + GetterSetter* asGetterSetter(JSValue); + + inline GetterSetter* asGetterSetter(JSValue value) + { + ASSERT(value.asCell()->isGetterSetter()); + return static_cast<GetterSetter*>(value.asCell()); + } + + +} // namespace JSC + +#endif // GetterSetter_h diff --git a/Source/JavaScriptCore/runtime/GlobalEvalFunction.cpp b/Source/JavaScriptCore/runtime/GlobalEvalFunction.cpp new file mode 100644 index 0000000..3ad4644 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GlobalEvalFunction.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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 "GlobalEvalFunction.h" + +#include "JSGlobalObject.h" +#include <wtf/Assertions.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(GlobalEvalFunction); + +GlobalEvalFunction::GlobalEvalFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, int len, const Identifier& name, NativeFunction function, JSGlobalObject* cachedGlobalObject) + : PrototypeFunction(exec, globalObject, structure, len, name, function) + , m_cachedGlobalObject(cachedGlobalObject) +{ + ASSERT_ARG(cachedGlobalObject, cachedGlobalObject); +} + +void GlobalEvalFunction::markChildren(MarkStack& markStack) +{ + PrototypeFunction::markChildren(markStack); + markStack.append(m_cachedGlobalObject); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/GlobalEvalFunction.h b/Source/JavaScriptCore/runtime/GlobalEvalFunction.h new file mode 100644 index 0000000..b889ca9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GlobalEvalFunction.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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. + * + */ + +#ifndef GlobalEvalFunction_h +#define GlobalEvalFunction_h + +#include "PrototypeFunction.h" + +namespace JSC { + + class JSGlobalObject; + + class GlobalEvalFunction : public PrototypeFunction { + public: + GlobalEvalFunction(ExecState*, JSGlobalObject*, 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, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = ImplementsHasInstance | OverridesMarkChildren | OverridesGetPropertyNames | PrototypeFunction::StructureFlags; + + private: + virtual void markChildren(MarkStack&); + + JSGlobalObject* m_cachedGlobalObject; + }; + +} // namespace JSC + +#endif // GlobalEvalFunction_h diff --git a/Source/JavaScriptCore/runtime/Identifier.cpp b/Source/JavaScriptCore/runtime/Identifier.cpp new file mode 100644 index 0000000..28cfd0a --- /dev/null +++ b/Source/JavaScriptCore/runtime/Identifier.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 + * 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 "Identifier.h" + +#include "CallFrame.h" +#include "NumericStrings.h" +#include <new> // for placement new +#include <string.h> // for strlen +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/HashSet.h> +#include <wtf/WTFThreadData.h> +#include <wtf/text/StringHash.h> + +using WTF::ThreadSpecific; + +namespace JSC { + +IdentifierTable::~IdentifierTable() +{ + HashSet<StringImpl*>::iterator end = m_table.end(); + for (HashSet<StringImpl*>::iterator iter = m_table.begin(); iter != end; ++iter) + (*iter)->setIsIdentifier(false); +} +std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(StringImpl* value) +{ + std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add(value); + (*result.first)->setIsIdentifier(true); + return result; +} +template<typename U, typename V> +std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(U value) +{ + std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add<U, V>(value); + (*result.first)->setIsIdentifier(true); + return result; +} + +IdentifierTable* createIdentifierTable() +{ + return new IdentifierTable; +} + +void deleteIdentifierTable(IdentifierTable* table) +{ + delete table; +} + +bool Identifier::equal(const StringImpl* r, const char* s) +{ + int length = r->length(); + const UChar* d = r->characters(); + for (int i = 0; i != length; ++i) + if (d[i] != (unsigned char)s[i]) + return false; + return s[length] == 0; +} + +bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length) +{ + if (r->length() != length) + return false; + const UChar* d = r->characters(); + for (unsigned i = 0; i != length; ++i) + if (d[i] != s[i]) + return false; + return true; +} + +struct IdentifierCStringTranslator { + static unsigned hash(const char* c) + { + return WTF::StringHasher::createHash<char>(c); + } + + static bool equal(StringImpl* r, const char* s) + { + return Identifier::equal(r, s); + } + + static void translate(StringImpl*& location, const char* c, unsigned hash) + { + size_t length = strlen(c); + UChar* d; + StringImpl* r = StringImpl::createUninitialized(length, d).leakRef(); + for (size_t i = 0; i != length; i++) + d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend + r->setHash(hash); + location = r; + } +}; + +PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const char* c) +{ + if (!c) + return 0; + if (!c[0]) + return StringImpl::empty(); + if (!c[1]) + return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0]))); + + IdentifierTable& identifierTable = *globalData->identifierTable; + LiteralIdentifierTable& literalIdentifierTable = identifierTable.literalTable(); + + const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c); + if (iter != literalIdentifierTable.end()) + return iter->second; + + pair<HashSet<StringImpl*>::iterator, bool> addResult = identifierTable.add<const char*, IdentifierCStringTranslator>(c); + + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + RefPtr<StringImpl> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; + + literalIdentifierTable.add(c, addedString.get()); + + return addedString.release(); +} + +PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const char* c) +{ + return add(&exec->globalData(), c); +} + +struct UCharBuffer { + const UChar* s; + unsigned int length; +}; + +struct IdentifierUCharBufferTranslator { + static unsigned hash(const UCharBuffer& buf) + { + return WTF::StringHasher::createHash<UChar>(buf.s, buf.length); + } + + static bool equal(StringImpl* str, const UCharBuffer& buf) + { + return Identifier::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash) + { + UChar* d; + StringImpl* r = StringImpl::createUninitialized(buf.length, d).leakRef(); + for (unsigned i = 0; i != buf.length; i++) + d[i] = buf.s[i]; + r->setHash(hash); + location = r; + } +}; + +uint32_t Identifier::toUInt32(const UString& string, bool& ok) +{ + ok = false; + + unsigned length = string.length(); + const UChar* characters = string.characters(); + + // An empty string is not a number. + if (!length) + return 0; + + // Get the first character, turning it into a digit. + uint32_t value = characters[0] - '0'; + if (value > 9) + return 0; + + // Check for leading zeros. If the first characher is 0, then the + // length of the string must be one - e.g. "042" is not equal to "42". + if (!value && length > 1) + return 0; + + while (--length) { + // Multiply value by 10, checking for overflow out of 32 bits. + if (value > 0xFFFFFFFFU / 10) + return 0; + value *= 10; + + // Get the next character, turning it into a digit. + uint32_t newValue = *(++characters) - '0'; + if (newValue > 9) + return 0; + + // Add in the old value, checking for overflow out of 32 bits. + newValue += value; + if (newValue < value) + return 0; + value = newValue; + } + + ok = true; + return value; +} + +PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const UChar* s, int length) +{ + if (length == 1) { + UChar c = s[0]; + if (c <= 0xFF) + return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); + } + if (!length) + return StringImpl::empty(); + UCharBuffer buf = {s, length}; + pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<UCharBuffer, IdentifierUCharBufferTranslator>(buf); + + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + return addResult.second ? adoptRef(*addResult.first) : *addResult.first; +} + +PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const UChar* s, int length) +{ + return add(&exec->globalData(), s, length); +} + +PassRefPtr<StringImpl> Identifier::addSlowCase(JSGlobalData* globalData, StringImpl* r) +{ + ASSERT(!r->isIdentifier()); + // The empty & null strings are static singletons, and static strings are handled + // in ::add() in the header, so we should never get here with a zero length string. + ASSERT(r->length()); + + if (r->length() == 1) { + UChar c = r->characters()[0]; + if (c <= 0xFF) + r = globalData->smallStrings.singleCharacterStringRep(c); + if (r->isIdentifier()) + return r; + } + + return *globalData->identifierTable->add(r).first; +} + +PassRefPtr<StringImpl> Identifier::addSlowCase(ExecState* exec, StringImpl* r) +{ + return addSlowCase(&exec->globalData(), r); +} + +Identifier Identifier::from(ExecState* exec, unsigned value) +{ + return Identifier(exec, exec->globalData().numericStrings.add(value)); +} + +Identifier Identifier::from(ExecState* exec, int value) +{ + return Identifier(exec, exec->globalData().numericStrings.add(value)); +} + +Identifier Identifier::from(ExecState* exec, double value) +{ + return Identifier(exec, exec->globalData().numericStrings.add(value)); +} + +Identifier Identifier::from(JSGlobalData* globalData, unsigned value) +{ + return Identifier(globalData, globalData->numericStrings.add(value)); +} + +Identifier Identifier::from(JSGlobalData* globalData, int value) +{ + return Identifier(globalData, globalData->numericStrings.add(value)); +} + +Identifier Identifier::from(JSGlobalData* globalData, double value) +{ + return Identifier(globalData, globalData->numericStrings.add(value)); +} + +#ifndef NDEBUG + +void Identifier::checkCurrentIdentifierTable(JSGlobalData* globalData) +{ + // Check the identifier table accessible through the threadspecific matches the + // globalData's identifier table. + ASSERT_UNUSED(globalData, globalData->identifierTable == wtfThreadData().currentIdentifierTable()); +} + +void Identifier::checkCurrentIdentifierTable(ExecState* exec) +{ + checkCurrentIdentifierTable(&exec->globalData()); +} + +#else + +// These only exists so that our exports are the same for debug and release builds. +// This would be an ASSERT_NOT_REACHED(), but we're in NDEBUG only code here! +void Identifier::checkCurrentIdentifierTable(JSGlobalData*) { CRASH(); } +void Identifier::checkCurrentIdentifierTable(ExecState*) { CRASH(); } + +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h new file mode 100644 index 0000000..3a8aed7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Identifier.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2003, 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 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. + * + */ + +#ifndef Identifier_h +#define Identifier_h + +#include "JSGlobalData.h" +#include "ThreadSpecific.h" +#include "UString.h" +#include <wtf/text/CString.h> + +namespace JSC { + + class ExecState; + + class Identifier { + friend class Structure; + public: + Identifier() { } + + Identifier(ExecState* exec, const char* s) : m_string(add(exec, s)) { } // Only to be used with string literals. + Identifier(ExecState* exec, const UChar* s, int length) : m_string(add(exec, s, length)) { } + Identifier(ExecState* exec, StringImpl* rep) : m_string(add(exec, rep)) { } + Identifier(ExecState* exec, const UString& s) : m_string(add(exec, s.impl())) { } + + Identifier(JSGlobalData* globalData, const char* s) : m_string(add(globalData, s)) { } // Only to be used with string literals. + Identifier(JSGlobalData* globalData, const UChar* s, int length) : m_string(add(globalData, s, length)) { } + Identifier(JSGlobalData* globalData, StringImpl* rep) : m_string(add(globalData, rep)) { } + Identifier(JSGlobalData* globalData, const UString& s) : m_string(add(globalData, s.impl())) { } + + const UString& ustring() const { return m_string; } + StringImpl* impl() const { return m_string.impl(); } + + const UChar* characters() const { return m_string.characters(); } + int length() const { return m_string.length(); } + + CString ascii() const { return m_string.ascii(); } + + static Identifier from(ExecState* exec, unsigned y); + static Identifier from(ExecState* exec, int y); + static Identifier from(ExecState* exec, double y); + static Identifier from(JSGlobalData*, unsigned y); + static Identifier from(JSGlobalData*, int y); + static Identifier from(JSGlobalData*, double y); + + static uint32_t toUInt32(const UString&, bool& ok); + uint32_t toUInt32(bool& ok) const { return toUInt32(m_string, ok); } + unsigned toArrayIndex(bool& ok) const; + + bool isNull() const { return m_string.isNull(); } + bool isEmpty() const { return m_string.isEmpty(); } + + friend bool operator==(const Identifier&, const Identifier&); + friend bool operator!=(const Identifier&, const Identifier&); + + friend bool operator==(const Identifier&, const char*); + friend bool operator!=(const Identifier&, const char*); + + static bool equal(const StringImpl*, const char*); + static bool equal(const StringImpl*, const UChar*, unsigned length); + static bool equal(const StringImpl* a, const StringImpl* b) { return ::equal(a, b); } + + static PassRefPtr<StringImpl> add(ExecState*, const char*); // Only to be used with string literals. + static PassRefPtr<StringImpl> add(JSGlobalData*, const char*); // Only to be used with string literals. + + private: + UString m_string; + + static bool equal(const Identifier& a, const Identifier& b) { return a.m_string.impl() == b.m_string.impl(); } + static bool equal(const Identifier& a, const char* b) { return equal(a.m_string.impl(), b); } + + static PassRefPtr<StringImpl> add(ExecState*, const UChar*, int length); + static PassRefPtr<StringImpl> add(JSGlobalData*, const UChar*, int length); + + static PassRefPtr<StringImpl> add(ExecState* exec, StringImpl* r) + { +#ifndef NDEBUG + checkCurrentIdentifierTable(exec); +#endif + if (r->isIdentifier()) + return r; + return addSlowCase(exec, r); + } + static PassRefPtr<StringImpl> add(JSGlobalData* globalData, StringImpl* r) + { +#ifndef NDEBUG + checkCurrentIdentifierTable(globalData); +#endif + if (r->isIdentifier()) + return r; + return addSlowCase(globalData, r); + } + + static PassRefPtr<StringImpl> addSlowCase(ExecState*, StringImpl* r); + static PassRefPtr<StringImpl> addSlowCase(JSGlobalData*, StringImpl* r); + + static void checkCurrentIdentifierTable(ExecState*); + static void checkCurrentIdentifierTable(JSGlobalData*); + }; + + inline bool operator==(const Identifier& a, const Identifier& b) + { + return Identifier::equal(a, b); + } + + inline bool operator!=(const Identifier& a, const Identifier& b) + { + return !Identifier::equal(a, b); + } + + inline bool operator==(const Identifier& a, const char* b) + { + return Identifier::equal(a, b); + } + + inline bool operator!=(const Identifier& a, const char* b) + { + return !Identifier::equal(a, b); + } + + IdentifierTable* createIdentifierTable(); + void deleteIdentifierTable(IdentifierTable*); + + struct IdentifierRepHash : PtrHash<RefPtr<StringImpl> > { + static unsigned hash(const RefPtr<StringImpl>& key) { return key->existingHash(); } + static unsigned hash(StringImpl* key) { return key->existingHash(); } + }; + +} // namespace JSC + +#endif // Identifier_h diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp new file mode 100644 index 0000000..08dddc1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "InitializeThreading.h" + +#include "Collector.h" +#include "dtoa.h" +#include "Identifier.h" +#include "JSGlobalObject.h" +#include "UString.h" +#include <wtf/DateMath.h> +#include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> + +using namespace WTF; + +namespace JSC { + +#if OS(DARWIN) && ENABLE(JSC_MULTIPLE_THREADS) +static pthread_once_t initializeThreadingKeyOnce = PTHREAD_ONCE_INIT; +#endif + +static void initializeThreadingOnce() +{ + // StringImpl::empty() does not construct its static string in a threadsafe fashion, + // so ensure it has been initialized from here. + StringImpl::empty(); + + WTF::initializeThreading(); + wtfThreadData(); + JSGlobalData::storeVPtrs(); +#if ENABLE(JSC_MULTIPLE_THREADS) + s_dtoaP5Mutex = new Mutex; + initializeDates(); + RegisterFile::initializeThreading(); +#endif +} + +void initializeThreading() +{ +#if OS(DARWIN) && ENABLE(JSC_MULTIPLE_THREADS) + pthread_once(&initializeThreadingKeyOnce, initializeThreadingOnce); +#else + static bool initializedThreading = false; + if (!initializedThreading) { + initializeThreadingOnce(); + initializedThreading = true; + } +#endif +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.h b/Source/JavaScriptCore/runtime/InitializeThreading.h new file mode 100644 index 0000000..1a93ccb --- /dev/null +++ b/Source/JavaScriptCore/runtime/InitializeThreading.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 InitializeThreading_h +#define InitializeThreading_h + +namespace JSC { + + // This function must be called from the main thread. It is safe to call it repeatedly. + // Darwin is an exception to this rule: it is OK to call this function from any thread, even reentrantly. + void initializeThreading(); + +} + +#endif // InitializeThreading_h diff --git a/Source/JavaScriptCore/runtime/InternalFunction.cpp b/Source/JavaScriptCore/runtime/InternalFunction.cpp new file mode 100644 index 0000000..0a8d9de --- /dev/null +++ b/Source/JavaScriptCore/runtime/InternalFunction.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 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 + * 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 "InternalFunction.h" + +#include "FunctionPrototype.h" +#include "JSGlobalObject.h" +#include "JSString.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(InternalFunction); + +const ClassInfo InternalFunction::info = { "Function", 0, 0, 0 }; + +const ClassInfo* InternalFunction::classInfo() const +{ + return &info; +} + +InternalFunction::InternalFunction(NonNullPassRefPtr<Structure> structure) + : JSObjectWithGlobalObject(structure) +{ +} + +InternalFunction::InternalFunction(JSGlobalData* globalData, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const Identifier& name) + : JSObjectWithGlobalObject(globalObject, structure) +{ + putDirect(globalData->propertyNames->name, jsString(globalData, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); +} + +const UString& InternalFunction::name(ExecState* exec) +{ + return asString(getDirect(exec->globalData().propertyNames->name))->tryGetValue(); +} + +const UString InternalFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->globalData().propertyNames->displayName); + + if (displayName && isJSString(&exec->globalData(), displayName)) + return asString(displayName)->tryGetValue(); + + return UString(); +} + +const UString InternalFunction::calculatedDisplayName(ExecState* exec) +{ + const UString explicitName = displayName(exec); + + if (!explicitName.isEmpty()) + return explicitName; + + return name(exec); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/InternalFunction.h b/Source/JavaScriptCore/runtime/InternalFunction.h new file mode 100644 index 0000000..401f17b --- /dev/null +++ b/Source/JavaScriptCore/runtime/InternalFunction.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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. + * + */ + +#ifndef InternalFunction_h +#define InternalFunction_h + +#include "JSObjectWithGlobalObject.h" +#include "Identifier.h" + +namespace JSC { + + class FunctionPrototype; + + class InternalFunction : public JSObjectWithGlobalObject { + public: + virtual const ClassInfo* classInfo() const; + static JS_EXPORTDATA const ClassInfo info; + + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); + + static PassRefPtr<Structure> createStructure(JSValue proto) + { + return Structure::create(proto, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = ImplementsHasInstance | JSObject::StructureFlags; + + // Only used to allow us to determine the JSFunction vptr + InternalFunction(NonNullPassRefPtr<Structure> structure); + + InternalFunction(JSGlobalData*, JSGlobalObject*, NonNullPassRefPtr<Structure>, const Identifier&); + + private: + virtual CallType getCallData(CallData&) = 0; + }; + + InternalFunction* asInternalFunction(JSValue); + + inline InternalFunction* asInternalFunction(JSValue value) + { + ASSERT(asObject(value)->inherits(&InternalFunction::info)); + return static_cast<InternalFunction*>(asObject(value)); + } + +} // namespace JSC + +#endif // InternalFunction_h diff --git a/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp new file mode 100644 index 0000000..e83724a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 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 + * 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 "JSAPIValueWrapper.h" + +#include "NumberObject.h" +#include "UString.h" + +namespace JSC { + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h new file mode 100644 index 0000000..10ded4c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2007, 2008 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 + * 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. + * + */ + +#ifndef JSAPIValueWrapper_h +#define JSAPIValueWrapper_h + +#include "JSCell.h" +#include "CallFrame.h" + +namespace JSC { + + class JSAPIValueWrapper : public JSCell { + friend JSValue jsAPIValueWrapper(ExecState*, JSValue); + public: + JSValue value() const { return m_value; } + + virtual bool isAPIValueWrapper() const { return true; } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(CompoundType, OverridesMarkChildren | OverridesGetPropertyNames), AnonymousSlotCount); + } + + + private: + JSAPIValueWrapper(ExecState* exec, JSValue value) + : JSCell(exec->globalData().apiWrapperStructure.get()) + , m_value(value) + { + ASSERT(!value.isCell()); + } + + JSValue m_value; + }; + + inline JSValue jsAPIValueWrapper(ExecState* exec, JSValue value) + { + return new (exec) JSAPIValueWrapper(exec, value); + } + +} // namespace JSC + +#endif // JSAPIValueWrapper_h diff --git a/Source/JavaScriptCore/runtime/JSActivation.cpp b/Source/JavaScriptCore/runtime/JSActivation.cpp new file mode 100644 index 0000000..1147858 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSActivation.cpp @@ -0,0 +1,223 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JSActivation.h" + +#include "Arguments.h" +#include "Interpreter.h" +#include "JSFunction.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSActivation); + +const ClassInfo JSActivation::info = { "JSActivation", 0, 0, 0 }; + +JSActivation::JSActivation(CallFrame* callFrame, NonNullPassRefPtr<FunctionExecutable> functionExecutable) + : Base(callFrame->globalData().activationStructure, new JSActivationData(functionExecutable, callFrame->registers())) +{ +} + +JSActivation::~JSActivation() +{ + delete d(); +} + +void JSActivation::markChildren(MarkStack& markStack) +{ + Base::markChildren(markStack); + + Register* registerArray = d()->registerArray.get(); + if (!registerArray) + return; + + size_t numParametersMinusThis = d()->functionExecutable->parameterCount(); + + size_t count = numParametersMinusThis; + markStack.appendValues(registerArray, count); + + size_t numVars = d()->functionExecutable->capturedVariableCount(); + + // Skip the call frame, which sits between the parameters and vars. + markStack.appendValues(registerArray + count + RegisterFile::CallFrameHeaderSize, numVars, MayContainNullValues); +} + +inline bool JSActivation::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) +{ + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + ASSERT(entry.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); + slot.setRegisterSlot(®isterAt(entry.getIndex())); + return true; + } + return false; +} + +inline bool JSActivation::symbolTablePut(const Identifier& propertyName, JSValue value) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (entry.isNull()) + return false; + if (entry.isReadOnly()) + return true; + ASSERT(entry.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); + registerAt(entry.getIndex()) = value; + return true; +} + +void JSActivation::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + SymbolTable::const_iterator end = symbolTable().end(); + for (SymbolTable::const_iterator it = symbolTable().begin(); it != end; ++it) { + ASSERT(it->second.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); + if (!(it->second.getAttributes() & DontEnum) || (mode == IncludeDontEnumProperties)) + propertyNames.add(Identifier(exec, it->first.get())); + } + // Skip the JSVariableObject implementation of getOwnPropertyNames + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +inline bool JSActivation::symbolTablePutWithAttributes(const Identifier& propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTable::iterator iter = symbolTable().find(propertyName.impl()); + if (iter == symbolTable().end()) + return false; + SymbolTableEntry& entry = iter->second; + ASSERT(!entry.isNull()); + if (entry.getIndex() >= static_cast<int>(d()->functionExecutable->capturedVariableCount())) + return false; + entry.setAttributes(attributes); + registerAt(entry.getIndex()) = value; + return true; +} + +bool JSActivation::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().arguments) { + slot.setCustom(this, getArgumentsGetter()); + return true; + } + + if (symbolTableGet(propertyName, slot)) + return true; + + if (JSValue* location = getDirectLocation(propertyName)) { + slot.setValueSlot(location); + return true; + } + + // We don't call through to JSObject because there's no way to give an + // activation object getter properties or a prototype. + ASSERT(!hasGetterSetterProperties()); + ASSERT(prototype().isNull()); + return false; +} + +void JSActivation::put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + if (symbolTablePut(propertyName, value)) + return; + + // We don't call through to JSObject because __proto__ and getter/setter + // properties are non-standard extensions that other implementations do not + // expose in the activation object. + ASSERT(!hasGetterSetterProperties()); + putDirect(propertyName, value, 0, true, slot); +} + +// FIXME: Make this function honor ReadOnly (const) and DontEnum +void JSActivation::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + if (symbolTablePutWithAttributes(propertyName, value, attributes)) + return; + + // We don't call through to JSObject because __proto__ and getter/setter + // properties are non-standard extensions that other implementations do not + // expose in the activation object. + ASSERT(!hasGetterSetterProperties()); + PutPropertySlot slot; + JSObject::putWithAttributes(exec, propertyName, value, attributes, true, slot); +} + +bool JSActivation::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().arguments) + return false; + + return Base::deleteProperty(exec, propertyName); +} + +JSObject* JSActivation::toThisObject(ExecState* exec) const +{ + return exec->globalThisValue(); +} + +JSValue JSActivation::toStrictThisObject(ExecState*) const +{ + return jsNull(); +} + +bool JSActivation::isDynamicScope(bool& requiresDynamicChecks) const +{ + requiresDynamicChecks = d()->functionExecutable->usesEval(); + return false; +} + +JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, const Identifier&) +{ + JSActivation* activation = asActivation(slotBase); + CallFrame* callFrame = CallFrame::create(activation->d()->registers); + int argumentsRegister = activation->d()->functionExecutable->generatedBytecode().argumentsRegister(); + if (!callFrame->uncheckedR(argumentsRegister).jsValue()) { + JSValue arguments = JSValue(new (callFrame) Arguments(callFrame)); + callFrame->uncheckedR(argumentsRegister) = arguments; + callFrame->uncheckedR(unmodifiedArgumentsRegister(argumentsRegister)) = arguments; + } + + ASSERT(callFrame->uncheckedR(argumentsRegister).jsValue().inherits(&Arguments::info)); + return callFrame->uncheckedR(argumentsRegister).jsValue(); +} + +// These two functions serve the purpose of isolating the common case from a +// PIC branch. + +PropertySlot::GetValueFunc JSActivation::getArgumentsGetter() +{ + return argumentsGetter; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSActivation.h b/Source/JavaScriptCore/runtime/JSActivation.h new file mode 100644 index 0000000..6dd6d70 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSActivation.h @@ -0,0 +1,115 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 JSActivation_h +#define JSActivation_h + +#include "CodeBlock.h" +#include "JSVariableObject.h" +#include "SymbolTable.h" +#include "Nodes.h" + +namespace JSC { + + class Arguments; + class Register; + + class JSActivation : public JSVariableObject { + typedef JSVariableObject Base; + public: + JSActivation(CallFrame*, NonNullPassRefPtr<FunctionExecutable>); + virtual ~JSActivation(); + + virtual void markChildren(MarkStack&); + + virtual bool isDynamicScope(bool& requiresDynamicChecks) const; + + virtual bool isActivationObject() const { return true; } + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode); + + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + virtual void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + + virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; + + void copyRegisters(); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | NeedsThisConversion | OverridesMarkChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; + + private: + struct JSActivationData : public JSVariableObjectData { + JSActivationData(NonNullPassRefPtr<FunctionExecutable> _functionExecutable, Register* registers) + : JSVariableObjectData(_functionExecutable->symbolTable(), registers) + , functionExecutable(_functionExecutable) + { + // We have to manually ref and deref the symbol table as JSVariableObjectData + // doesn't know about SharedSymbolTable + functionExecutable->symbolTable()->ref(); + } + ~JSActivationData() + { + static_cast<SharedSymbolTable*>(symbolTable)->deref(); + } + + RefPtr<FunctionExecutable> functionExecutable; + }; + + 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); + + static JSValue argumentsGetter(ExecState*, JSValue, const Identifier&); + NEVER_INLINE PropertySlot::GetValueFunc getArgumentsGetter(); + + JSActivationData* d() const { return static_cast<JSActivationData*>(JSVariableObject::d); } + }; + + JSActivation* asActivation(JSValue); + + inline JSActivation* asActivation(JSValue value) + { + ASSERT(asObject(value)->inherits(&JSActivation::info)); + return static_cast<JSActivation*>(asObject(value)); + } + +} // namespace JSC + +#endif // JSActivation_h diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp new file mode 100644 index 0000000..556a16e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -0,0 +1,1322 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "JSArray.h" + +#include "ArrayPrototype.h" +#include "CachedCall.h" +#include "Error.h" +#include "Executable.h" +#include "PropertyNameArray.h" +#include <wtf/AVLTree.h> +#include <wtf/Assertions.h> +#include <wtf/OwnPtr.h> +#include <Operations.h> + +using namespace std; +using namespace WTF; + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSArray); + +// Overview of JSArray +// +// Properties of JSArray objects may be stored in one of three locations: +// * The regular JSObject property map. +// * A storage vector. +// * A sparse map of array entries. +// +// Properties with non-numeric identifiers, with identifiers that are not representable +// as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX +// (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit +// integer) are not considered array indices and will be stored in the JSObject property map. +// +// All properties with a numeric identifer, representable as an unsigned integer i, +// where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the +// storage vector or the sparse map. An array index i will be handled in the following +// fashion: +// +// * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector. +// * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either +// be stored in the storage vector or in the sparse array, depending on the density of +// data that would be stored in the vector (a vector being used where at least +// (1 / minDensityMultiplier) of the entries would be populated). +// * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored +// in the sparse array. + +// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize +// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage +// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValue)) + +// (vectorLength * sizeof(JSValue)) must be <= 0xFFFFFFFFU (which is maximum value of size_t). +#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue)) + +// These values have to be macros to be used in max() and min() without introducing +// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. +#define MIN_SPARSE_ARRAY_INDEX 10000U +#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1) +// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer. +#define MAX_ARRAY_INDEX 0xFFFFFFFEU + +// The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate +// for an array that was created with a sepcified length (e.g. a = new Array(123)) +#define BASE_VECTOR_LEN 4U + +// The upper bound to the size we'll grow a zero length array when the first element +// is added. +#define FIRST_VECTOR_GROW 4U + +// Our policy for when to use a vector and when to use a sparse map. +// For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector. +// When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector +// as long as it is 1/8 full. If more sparse than that, we use a map. +static const unsigned minDensityMultiplier = 8; + +const ClassInfo JSArray::info = {"Array", 0, 0, 0}; + +// We keep track of the size of the last array after it was grown. We use this +// as a simple heuristic for as the value to grow the next array from size 0. +// This value is capped by the constant FIRST_VECTOR_GROW defined above. +static unsigned lastArraySize = 0; + +static inline size_t storageSize(unsigned vectorLength) +{ + ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH); + + // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH) + // - as asserted above - the following calculation cannot overflow. + size_t size = (sizeof(ArrayStorage) - sizeof(JSValue)) + (vectorLength * sizeof(JSValue)); + // Assertion to detect integer overflow in previous calculation (should not be possible, provided that + // MAX_STORAGE_VECTOR_LENGTH is correctly defined). + ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValue)))); + + return size; +} + +static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) +{ + return length / minDensityMultiplier <= numValues; +} + +#if !CHECK_ARRAY_CONSISTENCY + +inline void JSArray::checkConsistency(ConsistencyCheckType) +{ +} + +#endif + +JSArray::JSArray(VPtrStealingHackType) + : JSObject(createStructure(jsNull())) +{ + unsigned initialCapacity = 0; + + m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); + m_storage->m_allocBase = m_storage; + m_indexBias = 0; + m_vectorLength = initialCapacity; + + checkConsistency(); + + // It's not safe to call Heap::heap(this) in order to report extra memory + // cost here, because the VPtrStealingHackType JSArray is not allocated on + // the heap. For the same reason, it's OK not to report extra cost. +} + +JSArray::JSArray(NonNullPassRefPtr<Structure> structure) + : JSObject(structure) +{ + unsigned initialCapacity = 0; + + m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); + m_storage->m_allocBase = m_storage; + m_indexBias = 0; + m_vectorLength = initialCapacity; + + checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(0)); +} + +JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength, ArrayCreationMode creationMode) + : JSObject(structure) +{ + unsigned initialCapacity; + if (creationMode == CreateCompact) + initialCapacity = initialLength; + else + initialCapacity = min(BASE_VECTOR_LEN, MIN_SPARSE_ARRAY_INDEX); + + m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); + m_storage->m_allocBase = m_storage; + m_storage->m_length = initialLength; + m_indexBias = 0; + m_vectorLength = initialCapacity; + m_storage->m_sparseValueMap = 0; + m_storage->subclassData = 0; + m_storage->reportedMapCapacity = 0; + + if (creationMode == CreateCompact) { +#if CHECK_ARRAY_CONSISTENCY + m_storage->m_inCompactInitialization = !!initialCapacity; +#endif + m_storage->m_length = 0; + m_storage->m_numValuesInVector = initialCapacity; + } else { +#if CHECK_ARRAY_CONSISTENCY + storage->m_inCompactInitialization = false; +#endif + m_storage->m_length = initialLength; + m_storage->m_numValuesInVector = 0; + JSValue* vector = m_storage->m_vector; + for (size_t i = 0; i < initialCapacity; ++i) + vector[i] = JSValue(); + } + + checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity)); +} + +JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list) + : JSObject(structure) +{ + unsigned initialCapacity = list.size(); + unsigned initialStorage; + + // If the ArgList is empty, allocate space for 3 entries. This value empirically + // works well for benchmarks. + if (!initialCapacity) + initialStorage = 3; + else + initialStorage = initialCapacity; + + m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialStorage))); + m_storage->m_allocBase = m_storage; + m_indexBias = 0; + m_storage->m_length = initialCapacity; + m_vectorLength = initialStorage; + m_storage->m_numValuesInVector = initialCapacity; + m_storage->m_sparseValueMap = 0; + m_storage->subclassData = 0; + m_storage->reportedMapCapacity = 0; +#if CHECK_ARRAY_CONSISTENCY + m_storage->m_inCompactInitialization = false; +#endif + + size_t i = 0; + JSValue* vector = m_storage->m_vector; + ArgList::const_iterator end = list.end(); + for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i) + vector[i] = *it; + for (; i < initialStorage; i++) + vector[i] = JSValue(); + + checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(initialStorage)); +} + +JSArray::~JSArray() +{ + ASSERT(vptr() == JSGlobalData::jsArrayVPtr); + checkConsistency(DestructorConsistencyCheck); + + delete m_storage->m_sparseValueMap; + fastFree(m_storage->m_allocBase); +} + +bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) +{ + ArrayStorage* storage = m_storage; + + if (i >= storage->m_length) { + if (i > MAX_ARRAY_INDEX) + return getOwnPropertySlot(exec, Identifier::from(exec, i), slot); + return false; + } + + if (i < m_vectorLength) { + JSValue& valueSlot = storage->m_vector[i]; + if (valueSlot) { + slot.setValueSlot(&valueSlot); + return true; + } + } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (i >= MIN_SPARSE_ARRAY_INDEX) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->end()) { + slot.setValueSlot(&it->second); + return true; + } + } + } + + return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot); +} + +bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setValue(jsNumber(length())); + return true; + } + + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) + return JSArray::getOwnPropertySlot(exec, i, slot); + + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(length()), DontDelete | DontEnum); + return true; + } + + ArrayStorage* storage = m_storage; + + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) { + if (i >= storage->m_length) + return false; + if (i < m_vectorLength) { + JSValue& value = storage->m_vector[i]; + if (value) { + descriptor.setDescriptor(value, 0); + return true; + } + } else if (SparseArrayValueMap* map = 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) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) { + put(exec, i, value); + return; + } + + if (propertyName == exec->propertyNames().length) { + unsigned newLength = value.toUInt32(exec); + if (value.toNumber(exec) != static_cast<double>(newLength)) { + throwError(exec, createRangeError(exec, "Invalid array length.")); + return; + } + setLength(newLength); + return; + } + + JSObject::put(exec, propertyName, value, slot); +} + +void JSArray::put(ExecState* exec, unsigned i, JSValue value) +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + unsigned length = storage->m_length; + if (i >= length && i <= MAX_ARRAY_INDEX) { + length = i + 1; + storage->m_length = length; + } + + if (i < m_vectorLength) { + JSValue& valueSlot = storage->m_vector[i]; + if (valueSlot) { + valueSlot = value; + checkConsistency(); + return; + } + valueSlot = value; + ++storage->m_numValuesInVector; + checkConsistency(); + return; + } + + putSlowCase(exec, i, value); +} + +NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value) +{ + ArrayStorage* storage = m_storage; + + SparseArrayValueMap* map = storage->m_sparseValueMap; + + if (i >= MIN_SPARSE_ARRAY_INDEX) { + if (i > MAX_ARRAY_INDEX) { + PutPropertySlot slot; + put(exec, Identifier::from(exec, i), value, slot); + return; + } + + // We miss some cases where we could compact the storage, such as a large array that is being filled from the end + // (which will only be compacted as we reach indices that are less than MIN_SPARSE_ARRAY_INDEX) - but this makes the check much faster. + if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) { + if (!map) { + map = new SparseArrayValueMap; + storage->m_sparseValueMap = map; + } + + pair<SparseArrayValueMap::iterator, bool> result = map->add(i, value); + if (!result.second) { // pre-existing entry + result.first->second = value; + return; + } + + size_t capacity = map->capacity(); + if (capacity != storage->reportedMapCapacity) { + Heap::heap(this)->reportExtraMemoryCost((capacity - storage->reportedMapCapacity) * (sizeof(unsigned) + sizeof(JSValue))); + storage->reportedMapCapacity = capacity; + } + return; + } + } + + // We have decided that we'll put the new item into the vector. + // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it. + if (!map || map->isEmpty()) { + if (increaseVectorLength(i + 1)) { + storage = m_storage; + storage->m_vector[i] = value; + ++storage->m_numValuesInVector; + checkConsistency(); + } else + throwOutOfMemoryError(exec); + return; + } + + // Decide how many values it would be best to move from the map. + unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; + unsigned newVectorLength = getNewVectorLength(i + 1); + 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); + if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { + unsigned needLength = max(i + 1, storage->m_length); + unsigned proposedNewNumValuesInVector = newNumValuesInVector; + // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further. + while ((newVectorLength < needLength) && (newVectorLength < MAX_STORAGE_VECTOR_LENGTH)) { + unsigned proposedNewVectorLength = getNewVectorLength(newVectorLength + 1); + for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j) + proposedNewNumValuesInVector += map->contains(j); + if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) + break; + newVectorLength = proposedNewVectorLength; + newNumValuesInVector = proposedNewNumValuesInVector; + } + } + + void* baseStorage = storage->m_allocBase; + + if (!tryFastRealloc(baseStorage, storageSize(newVectorLength + m_indexBias)).getValue(baseStorage)) { + throwOutOfMemoryError(exec); + return; + } + + m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + m_indexBias * sizeof(JSValue)); + m_storage->m_allocBase = baseStorage; + storage = m_storage; + + unsigned vectorLength = m_vectorLength; + JSValue* vector = storage->m_vector; + + if (newNumValuesInVector == storage->m_numValuesInVector + 1) { + for (unsigned j = vectorLength; j < newVectorLength; ++j) + vector[j] = JSValue(); + if (i > MIN_SPARSE_ARRAY_INDEX) + map->remove(i); + } else { + for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j) + vector[j] = JSValue(); + for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) + vector[j] = map->take(j); + } + + ASSERT(i < newVectorLength); + + m_vectorLength = newVectorLength; + storage->m_numValuesInVector = newNumValuesInVector; + + storage->m_vector[i] = value; + + checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); +} + +bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) + return deleteProperty(exec, i); + + if (propertyName == exec->propertyNames().length) + return false; + + return JSObject::deleteProperty(exec, propertyName); +} + +bool JSArray::deleteProperty(ExecState* exec, unsigned i) +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + if (i < m_vectorLength) { + JSValue& valueSlot = storage->m_vector[i]; + if (!valueSlot) { + checkConsistency(); + return false; + } + valueSlot = JSValue(); + --storage->m_numValuesInVector; + checkConsistency(); + return true; + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (i >= MIN_SPARSE_ARRAY_INDEX) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->end()) { + map->remove(it); + checkConsistency(); + return true; + } + } + } + + checkConsistency(); + + if (i > MAX_ARRAY_INDEX) + return deleteProperty(exec, Identifier::from(exec, i)); + + return false; +} + +void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + // FIXME: Filling PropertyNameArray with an identifier for every integer + // is incredibly inefficient for large arrays. We need a different approach, + // which almost certainly means a different structure for PropertyNameArray. + + ArrayStorage* storage = m_storage; + + 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)); + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) + propertyNames.add(Identifier::from(exec, it->first)); + } + + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().length); + + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength) +{ + ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); + + unsigned increasedLength; + unsigned maxInitLength = min(m_storage->m_length, 100000U); + + if (desiredLength < maxInitLength) + increasedLength = maxInitLength; + else if (!m_vectorLength) + increasedLength = max(desiredLength, lastArraySize); + else { + // Mathematically equivalent to: + // increasedLength = (newLength * 3 + 1) / 2; + // or: + // increasedLength = (unsigned)ceil(newLength * 1.5)); + // This form is not prone to internal overflow. + increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); + } + + ASSERT(increasedLength >= desiredLength); + + lastArraySize = min(increasedLength, FIRST_VECTOR_GROW); + + return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); +} + +bool JSArray::increaseVectorLength(unsigned newLength) +{ + // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map + // to the vector. Callers have to account for that, because they can do it more efficiently. + + ArrayStorage* storage = m_storage; + + unsigned vectorLength = m_vectorLength; + ASSERT(newLength > vectorLength); + ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX); + unsigned newVectorLength = getNewVectorLength(newLength); + void* baseStorage = storage->m_allocBase; + + if (!tryFastRealloc(baseStorage, storageSize(newVectorLength + m_indexBias)).getValue(baseStorage)) + return false; + + storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + m_indexBias * sizeof(JSValue)); + m_storage->m_allocBase = baseStorage; + + JSValue* vector = storage->m_vector; + for (unsigned i = vectorLength; i < newVectorLength; ++i) + vector[i] = JSValue(); + + m_vectorLength = newVectorLength; + + Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); + + return true; +} + +bool JSArray::increaseVectorPrefixLength(unsigned newLength) +{ + // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map + // to the vector. Callers have to account for that, because they can do it more efficiently. + + ArrayStorage* storage = m_storage; + + unsigned vectorLength = m_vectorLength; + ASSERT(newLength > vectorLength); + ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX); + unsigned newVectorLength = getNewVectorLength(newLength); + + void* newBaseStorage = fastMalloc(storageSize(newVectorLength + m_indexBias)); + if (!newBaseStorage) + return false; + + m_indexBias += newVectorLength - newLength; + + m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newBaseStorage) + m_indexBias * sizeof(JSValue)); + + memcpy(m_storage, storage, storageSize(0)); + memcpy(&m_storage->m_vector[newLength - m_vectorLength], &storage->m_vector[0], vectorLength * sizeof(JSValue)); + + m_storage->m_allocBase = newBaseStorage; + m_vectorLength = newLength; + + fastFree(storage->m_allocBase); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); + + return true; +} + + +void JSArray::setLength(unsigned newLength) +{ + ArrayStorage* storage = m_storage; + +#if CHECK_ARRAY_CONSISTENCY + if (!storage->m_inCompactInitialization) + checkConsistency(); + else + storage->m_inCompactInitialization = false; +#endif + + unsigned length = storage->m_length; + + if (newLength < length) { + unsigned usedVectorLength = min(length, m_vectorLength); + for (unsigned i = newLength; i < usedVectorLength; ++i) { + JSValue& valueSlot = storage->m_vector[i]; + bool hadValue = valueSlot; + valueSlot = JSValue(); + storage->m_numValuesInVector -= hadValue; + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap copy = *map; + SparseArrayValueMap::iterator end = copy.end(); + for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) { + if (it->first >= newLength) + map->remove(it->first); + } + if (map->isEmpty()) { + delete map; + storage->m_sparseValueMap = 0; + } + } + } + + storage->m_length = newLength; + + checkConsistency(); +} + +JSValue JSArray::pop() +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + unsigned length = storage->m_length; + if (!length) + return jsUndefined(); + + --length; + + JSValue result; + + if (length < m_vectorLength) { + JSValue& valueSlot = storage->m_vector[length]; + if (valueSlot) { + --storage->m_numValuesInVector; + result = valueSlot; + valueSlot = JSValue(); + } else + result = jsUndefined(); + } else { + result = jsUndefined(); + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator it = map->find(length); + if (it != map->end()) { + result = it->second; + map->remove(it); + if (map->isEmpty()) { + delete map; + storage->m_sparseValueMap = 0; + } + } + } + } + + storage->m_length = length; + + checkConsistency(); + + return result; +} + +void JSArray::push(ExecState* exec, JSValue value) +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + if (storage->m_length < m_vectorLength) { + storage->m_vector[storage->m_length] = value; + ++storage->m_numValuesInVector; + ++storage->m_length; + checkConsistency(); + return; + } + + if (storage->m_length < MIN_SPARSE_ARRAY_INDEX) { + SparseArrayValueMap* map = storage->m_sparseValueMap; + if (!map || map->isEmpty()) { + if (increaseVectorLength(storage->m_length + 1)) { + storage = m_storage; + storage->m_vector[storage->m_length] = value; + ++storage->m_numValuesInVector; + ++storage->m_length; + checkConsistency(); + return; + } + checkConsistency(); + throwOutOfMemoryError(exec); + return; + } + } + + putSlowCase(exec, storage->m_length++, value); +} + +void JSArray::shiftCount(ExecState* exec, int count) +{ + ASSERT(count > 0); + + ArrayStorage* storage = m_storage; + + unsigned oldLength = storage->m_length; + + if (!oldLength) + return; + + if (oldLength != storage->m_numValuesInVector) { + // If m_length and m_numValuesInVector aren't the same, we have a sparse vector + // which means we need to go through each entry looking for the the "empty" + // slots and then fill them with possible properties. See ECMA spec. + // 15.4.4.9 steps 11 through 13. + for (unsigned i = count; i < oldLength; ++i) { + if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { + PropertySlot slot(this); + JSValue p = prototype(); + if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) + put(exec, i, slot.getValue(exec, i)); + } + } + + storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. + + // Need to decrement numValuesInvector based on number of real entries + for (unsigned i = 0; i < (unsigned)count; ++i) + if ((i < m_vectorLength) && (storage->m_vector[i])) + --storage->m_numValuesInVector; + } else + storage->m_numValuesInVector -= count; + + storage->m_length -= count; + + if (m_vectorLength) { + count = min(m_vectorLength, (unsigned)count); + + m_vectorLength -= count; + + if (m_vectorLength) { + char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(JSValue); + memmove(newBaseStorage, storage, storageSize(0)); + m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); + + m_indexBias += count; + } + } +} + +void JSArray::unshiftCount(ExecState* exec, int count) +{ + ArrayStorage* storage = m_storage; + + ASSERT(m_indexBias >= 0); + ASSERT(count >= 0); + + unsigned length = storage->m_length; + + if (length != storage->m_numValuesInVector) { + // If m_length and m_numValuesInVector aren't the same, we have a sparse vector + // which means we need to go through each entry looking for the the "empty" + // slots and then fill them with possible properties. See ECMA spec. + // 15.4.4.13 steps 8 through 10. + for (unsigned i = 0; i < length; ++i) { + if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { + PropertySlot slot(this); + JSValue p = prototype(); + if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) + put(exec, i, slot.getValue(exec, i)); + } + } + } + + storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. + + if (m_indexBias >= count) { + m_indexBias -= count; + char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(JSValue); + memmove(newBaseStorage, storage, storageSize(0)); + m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); + m_vectorLength += count; + } else if (!increaseVectorPrefixLength(m_vectorLength + count)) { + throwOutOfMemoryError(exec); + return; + } + + JSValue* vector = m_storage->m_vector; + for (int i = 0; i < count; i++) + vector[i] = JSValue(); +} + +void JSArray::markChildren(MarkStack& markStack) +{ + markChildrenDirect(markStack); +} + +static int compareNumbersForQSort(const void* a, const void* b) +{ + double da = static_cast<const JSValue*>(a)->uncheckedGetNumber(); + double db = static_cast<const JSValue*>(b)->uncheckedGetNumber(); + return (da > db) - (da < db); +} + +static int compareByStringPairForQSort(const void* a, const void* b) +{ + const ValueStringPair* va = static_cast<const ValueStringPair*>(a); + const ValueStringPair* vb = static_cast<const ValueStringPair*>(b); + return codePointCompare(va->second, vb->second); +} + +void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) +{ + ArrayStorage* storage = m_storage; + + unsigned lengthNotIncludingUndefined = compactForSorting(); + if (storage->m_sparseValueMap) { + throwOutOfMemoryError(exec); + return; + } + + if (!lengthNotIncludingUndefined) + return; + + bool allValuesAreNumbers = true; + size_t size = storage->m_numValuesInVector; + for (size_t i = 0; i < size; ++i) { + if (!storage->m_vector[i].isNumber()) { + allValuesAreNumbers = false; + break; + } + } + + if (!allValuesAreNumbers) + return sort(exec, compareFunction, callType, callData); + + // For numeric comparison, which is fast, qsort is faster than mergesort. We + // also don't require mergesort's stability, since there's no user visible + // side-effect from swapping the order of equal primitive values. + qsort(storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort); + + checkConsistency(SortConsistencyCheck); +} + +void JSArray::sort(ExecState* exec) +{ + ArrayStorage* storage = m_storage; + + unsigned lengthNotIncludingUndefined = compactForSorting(); + if (storage->m_sparseValueMap) { + throwOutOfMemoryError(exec); + return; + } + + if (!lengthNotIncludingUndefined) + return; + + // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. + // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary + // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return + // random or otherwise changing results, effectively making compare function inconsistent. + + Vector<ValueStringPair> values(lengthNotIncludingUndefined); + if (!values.begin()) { + throwOutOfMemoryError(exec); + return; + } + + Heap::heap(this)->pushTempSortVector(&values); + + for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { + JSValue value = storage->m_vector[i]; + ASSERT(!value.isUndefined()); + values[i].first = value; + } + + // FIXME: The following loop continues to call toString on subsequent values even after + // a toString call raises an exception. + + for (size_t i = 0; i < lengthNotIncludingUndefined; i++) + values[i].second = values[i].first.toString(exec); + + if (exec->hadException()) + return; + + // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather + // than O(N log N). + +#if HAVE(MERGESORT) + mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); +#else + // FIXME: The qsort library function is likely to not be a stable sort. + // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. + qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); +#endif + + // If the toString function changed the length of the array or vector storage, + // increase the length to handle the orignal number of actual values. + if (m_vectorLength < lengthNotIncludingUndefined) + increaseVectorLength(lengthNotIncludingUndefined); + if (storage->m_length < lengthNotIncludingUndefined) + storage->m_length = lengthNotIncludingUndefined; + + for (size_t i = 0; i < lengthNotIncludingUndefined; i++) + storage->m_vector[i] = values[i].first; + + Heap::heap(this)->popTempSortVector(&values); + + checkConsistency(SortConsistencyCheck); +} + +struct AVLTreeNodeForArrayCompare { + JSValue value; + + // Child pointers. The high bit of gt is robbed and used as the + // balance factor sign. The high bit of lt is robbed and used as + // the magnitude of the balance factor. + int32_t gt; + int32_t lt; +}; + +struct AVLTreeAbstractorForArrayCompare { + typedef int32_t handle; // Handle is an index into m_nodes vector. + typedef JSValue key; + typedef int32_t size; + + Vector<AVLTreeNodeForArrayCompare> m_nodes; + ExecState* m_exec; + JSValue m_compareFunction; + CallType m_compareCallType; + const CallData* m_compareCallData; + JSValue m_globalThisValue; + OwnPtr<CachedCall> m_cachedCall; + + handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; } + void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; } + handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; } + void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; } + + int get_balance_factor(handle h) + { + if (m_nodes[h].gt & 0x80000000) + return -1; + return static_cast<unsigned>(m_nodes[h].lt) >> 31; + } + + void set_balance_factor(handle h, int bf) + { + if (bf == 0) { + m_nodes[h].lt &= 0x7FFFFFFF; + m_nodes[h].gt &= 0x7FFFFFFF; + } else { + m_nodes[h].lt |= 0x80000000; + if (bf < 0) + m_nodes[h].gt |= 0x80000000; + else + m_nodes[h].gt &= 0x7FFFFFFF; + } + } + + int compare_key_key(key va, key vb) + { + ASSERT(!va.isUndefined()); + ASSERT(!vb.isUndefined()); + + if (m_exec->hadException()) + return 1; + + double compareResult; + if (m_cachedCall) { + m_cachedCall->setThis(m_globalThisValue); + m_cachedCall->setArgument(0, va); + m_cachedCall->setArgument(1, vb); + compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec)); + } else { + MarkedArgumentBuffer arguments; + arguments.append(va); + arguments.append(vb); + compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments).toNumber(m_exec); + } + return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent. + } + + int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); } + int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); } + + static handle null() { return 0x7FFFFFFF; } +}; + +void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + // FIXME: This ignores exceptions raised in the compare function or in toNumber. + + // The maximum tree depth is compiled in - but the caller is clearly up to no good + // if a larger array is passed. + ASSERT(storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); + if (storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) + return; + + unsigned usedVectorLength = min(storage->m_length, m_vectorLength); + unsigned nodeCount = usedVectorLength + (storage->m_sparseValueMap ? storage->m_sparseValueMap->size() : 0); + + if (!nodeCount) + return; + + AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items + tree.abstractor().m_exec = exec; + tree.abstractor().m_compareFunction = compareFunction; + tree.abstractor().m_compareCallType = callType; + tree.abstractor().m_compareCallData = &callData; + tree.abstractor().m_globalThisValue = exec->globalThisValue(); + tree.abstractor().m_nodes.grow(nodeCount); + + if (callType == CallTypeJS) + tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, asFunction(compareFunction), 2)); + + if (!tree.abstractor().m_nodes.begin()) { + throwOutOfMemoryError(exec); + return; + } + + // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified + // right out from under us while we're building the tree here. + + unsigned numDefined = 0; + unsigned numUndefined = 0; + + // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. + for (; numDefined < usedVectorLength; ++numDefined) { + JSValue v = storage->m_vector[numDefined]; + if (!v || v.isUndefined()) + break; + tree.abstractor().m_nodes[numDefined].value = v; + tree.insert(numDefined); + } + for (unsigned i = numDefined; i < usedVectorLength; ++i) { + JSValue v = storage->m_vector[i]; + if (v) { + if (v.isUndefined()) + ++numUndefined; + else { + tree.abstractor().m_nodes[numDefined].value = v; + tree.insert(numDefined); + ++numDefined; + } + } + } + + unsigned newUsedVectorLength = numDefined + numUndefined; + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + newUsedVectorLength += map->size(); + 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); + return; + } + } + + storage = m_storage; + + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) { + tree.abstractor().m_nodes[numDefined].value = it->second; + tree.insert(numDefined); + ++numDefined; + } + + delete map; + storage->m_sparseValueMap = 0; + } + + ASSERT(tree.abstractor().m_nodes.size() >= numDefined); + + // FIXME: If the compare function changed the length of the array, the following might be + // modifying the vector incorrectly. + + // Copy the values back into m_storage. + AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; + iter.start_iter_least(tree); + for (unsigned i = 0; i < numDefined; ++i) { + storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value; + ++iter; + } + + // Put undefined values back in. + for (unsigned i = numDefined; i < newUsedVectorLength; ++i) + storage->m_vector[i] = jsUndefined(); + + // Ensure that unused values in the vector are zeroed out. + for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) + storage->m_vector[i] = JSValue(); + + storage->m_numValuesInVector = newUsedVectorLength; + + checkConsistency(SortConsistencyCheck); +} + +void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) +{ + ArrayStorage* storage = m_storage; + + JSValue* vector = storage->m_vector; + unsigned vectorEnd = min(storage->m_length, m_vectorLength); + unsigned i = 0; + for (; i < vectorEnd; ++i) { + JSValue& v = vector[i]; + if (!v) + break; + args.append(v); + } + + for (; i < storage->m_length; ++i) + args.append(get(exec, i)); +} + +void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize) +{ + ASSERT(m_storage->m_length >= maxSize); + UNUSED_PARAM(maxSize); + JSValue* vector = m_storage->m_vector; + unsigned vectorEnd = min(maxSize, m_vectorLength); + unsigned i = 0; + for (; i < vectorEnd; ++i) { + JSValue& v = vector[i]; + if (!v) + break; + buffer[i] = v; + } + + for (; i < maxSize; ++i) + buffer[i] = get(exec, i); +} + +unsigned JSArray::compactForSorting() +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + unsigned usedVectorLength = min(storage->m_length, m_vectorLength); + + unsigned numDefined = 0; + unsigned numUndefined = 0; + + for (; numDefined < usedVectorLength; ++numDefined) { + JSValue v = storage->m_vector[numDefined]; + if (!v || v.isUndefined()) + break; + } + for (unsigned i = numDefined; i < usedVectorLength; ++i) { + JSValue v = storage->m_vector[i]; + if (v) { + if (v.isUndefined()) + ++numUndefined; + else + storage->m_vector[numDefined++] = v; + } + } + + unsigned newUsedVectorLength = numDefined + numUndefined; + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + newUsedVectorLength += map->size(); + 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)) + return 0; + + storage = m_storage; + } + + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) + storage->m_vector[numDefined++] = it->second; + + delete map; + storage->m_sparseValueMap = 0; + } + + for (unsigned i = numDefined; i < newUsedVectorLength; ++i) + storage->m_vector[i] = jsUndefined(); + for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) + storage->m_vector[i] = JSValue(); + + storage->m_numValuesInVector = newUsedVectorLength; + + checkConsistency(SortConsistencyCheck); + + return numDefined; +} + +void* JSArray::subclassData() const +{ + return m_storage->subclassData; +} + +void JSArray::setSubclassData(void* d) +{ + m_storage->subclassData = d; +} + +#if CHECK_ARRAY_CONSISTENCY + +void JSArray::checkConsistency(ConsistencyCheckType type) +{ + ArrayStorage* storage = m_storage; + + ASSERT(storage); + if (type == SortConsistencyCheck) + ASSERT(!storage->m_sparseValueMap); + + unsigned numValuesInVector = 0; + for (unsigned i = 0; i < m_vectorLength; ++i) { + if (JSValue value = storage->m_vector[i]) { + ASSERT(i < storage->m_length); + if (type != DestructorConsistencyCheck) + value.isUndefined(); // Likely to crash if the object was deallocated. + ++numValuesInVector; + } else { + if (type == SortConsistencyCheck) + ASSERT(i >= storage->m_numValuesInVector); + } + } + ASSERT(numValuesInVector == storage->m_numValuesInVector); + ASSERT(numValuesInVector <= storage->m_length); + + if (storage->m_sparseValueMap) { + SparseArrayValueMap::iterator end = storage->m_sparseValueMap->end(); + for (SparseArrayValueMap::iterator it = storage->m_sparseValueMap->begin(); it != end; ++it) { + unsigned index = it->first; + ASSERT(index < storage->m_length); + ASSERT(index >= storage->m_vectorLength); + ASSERT(index <= MAX_ARRAY_INDEX); + ASSERT(it->second); + if (type != DestructorConsistencyCheck) + it->second.isUndefined(); // Likely to crash if the object was deallocated. + } + } +} + +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h new file mode 100644 index 0000000..de28b65 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * 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 Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef JSArray_h +#define JSArray_h + +#include "JSObject.h" + +#define CHECK_ARRAY_CONSISTENCY 0 + +namespace JSC { + + typedef HashMap<unsigned, JSValue> SparseArrayValueMap; + + // This struct holds the actual data values of an array. A JSArray object points to it's contained ArrayStorage + // struct by pointing to m_vector. To access the contained ArrayStorage struct, use the getStorage() and + // setStorage() methods. It is important to note that there may be space before the ArrayStorage that + // is used to quick unshift / shift operation. The actual allocated pointer is available by using: + // getStorage() - m_indexBias * sizeof(JSValue) + struct ArrayStorage { + unsigned m_length; // The "length" property on the array + unsigned m_numValuesInVector; + SparseArrayValueMap* m_sparseValueMap; + void* subclassData; // A JSArray subclass can use this to fill the vector lazily. + void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector. + size_t reportedMapCapacity; +#if CHECK_ARRAY_CONSISTENCY + bool m_inCompactInitialization; +#endif + JSValue m_vector[1]; + }; + + // The CreateCompact creation mode is used for fast construction of arrays + // whose size and contents are known at time of creation. + // + // There are two obligations when using this mode: + // + // - uncheckedSetIndex() must be used when initializing the array. + // - setLength() must be called after initialization. + + enum ArrayCreationMode { CreateCompact, CreateInitialized }; + + class JSArray : public JSObject { + friend class JIT; + friend class Walker; + + public: + enum VPtrStealingHackType { VPtrStealingHack }; + JSArray(VPtrStealingHackType); + + explicit JSArray(NonNullPassRefPtr<Structure>); + JSArray(NonNullPassRefPtr<Structure>, unsigned initialLength, ArrayCreationMode); + 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; + + unsigned length() const { return m_storage->m_length; } + void setLength(unsigned); // OK to use on new arrays, but not if it might be a RegExpMatchArray. + + void sort(ExecState*); + void sort(ExecState*, JSValue compareFunction, CallType, const CallData&); + void sortNumeric(ExecState*, JSValue compareFunction, CallType, const CallData&); + + void push(ExecState*, JSValue); + JSValue pop(); + + void shiftCount(ExecState*, int count); + void unshiftCount(ExecState*, int count); + + 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_vectorLength; } + void setIndex(unsigned i, JSValue v) + { + ASSERT(canSetIndex(i)); + + JSValue& x = m_storage->m_vector[i]; + if (!x) { + ArrayStorage *storage = m_storage; + ++storage->m_numValuesInVector; + if (i >= storage->m_length) + storage->m_length = i + 1; + } + x = v; + } + + void uncheckedSetIndex(unsigned i, JSValue v) + { + ASSERT(canSetIndex(i)); + ArrayStorage *storage = m_storage; +#if CHECK_ARRAY_CONSISTENCY + ASSERT(storage->m_inCompactInitialization); +#endif + storage->m_vector[i] = v; + } + + void fillArgList(ExecState*, MarkedArgumentBuffer&); + void copyToRegisters(ExecState*, Register*, uint32_t); + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + inline void markChildrenDirect(MarkStack& markStack); + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual bool deleteProperty(ExecState*, unsigned propertyName); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + virtual void markChildren(MarkStack&); + + void* subclassData() const; + void setSubclassData(void*); + + private: + virtual const ClassInfo* classInfo() const { return &info; } + + bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&); + void putSlowCase(ExecState*, unsigned propertyName, JSValue); + + unsigned getNewVectorLength(unsigned desiredLength); + bool increaseVectorLength(unsigned newLength); + bool increaseVectorPrefixLength(unsigned newLength); + + unsigned compactForSorting(); + + enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck }; + void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck); + + unsigned m_vectorLength; // The valid length of m_vector + int m_indexBias; // The number of JSValue sized blocks before ArrayStorage. + ArrayStorage *m_storage; + }; + + JSArray* asArray(JSValue); + + inline JSArray* asArray(JSCell* cell) + { + ASSERT(cell->inherits(&JSArray::info)); + return static_cast<JSArray*>(cell); + } + + inline JSArray* asArray(JSValue 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 void MarkStack::markChildren(JSCell* cell) + { + ASSERT(Heap::isCellMarked(cell)); + if (!cell->structure()->typeInfo().overridesMarkChildren()) { +#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() + { +#if !ASSERT_DISABLED + ASSERT(!m_isDraining); + m_isDraining = true; +#endif + 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::checkMarkCell(cell = value.asCell())) { + if (current.m_values == end) { + m_markSets.removeLast(); + continue; + } + goto findNextUnmarkedNullValue; + } + + 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()); + } +#if !ASSERT_DISABLED + m_isDraining = false; +#endif + } + + // Rule from ECMA 15.2 about what an array index is. + // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1. + inline unsigned Identifier::toArrayIndex(bool& ok) const + { + unsigned i = toUInt32(ok); + if (ok && i >= 0xFFFFFFFFU) + ok = false; + return i; + } + +} // namespace JSC + +#endif // JSArray_h diff --git a/Source/JavaScriptCore/runtime/JSByteArray.cpp b/Source/JavaScriptCore/runtime/JSByteArray.cpp new file mode 100644 index 0000000..6af9d75 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSByteArray.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSByteArray.h" + +#include "JSGlobalObject.h" +#include "PropertyNameArray.h" + +using namespace WTF; + +namespace JSC { + +const ClassInfo JSByteArray::s_defaultInfo = { "ByteArray", 0, 0, 0 }; + +JSByteArray::JSByteArray(ExecState* exec, NonNullPassRefPtr<Structure> structure, ByteArray* storage, const JSC::ClassInfo* classInfo) + : JSObject(structure) + , m_storage(storage) + , m_classInfo(classInfo) +{ + putDirect(exec->globalData().propertyNames->length, jsNumber(m_storage->length()), ReadOnly | DontDelete); +} + +#if !ASSERT_DISABLED +JSByteArray::~JSByteArray() +{ + ASSERT(vptr() == JSGlobalData::jsByteArrayVPtr); +} +#endif + + +PassRefPtr<Structure> JSByteArray::createStructure(JSValue prototype) +{ + PassRefPtr<Structure> result = Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + return result; +} + +bool JSByteArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + bool ok; + unsigned index = propertyName.toUInt32(ok); + if (ok && canAccessIndex(index)) { + slot.setValue(getIndex(exec, index)); + return true; + } + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool JSByteArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + bool ok; + unsigned index = propertyName.toUInt32(ok); + 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)) { + slot.setValue(getIndex(exec, propertyName)); + return true; + } + return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); +} + +void JSByteArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + bool ok; + unsigned index = propertyName.toUInt32(ok); + if (ok) { + setIndex(exec, index, value); + return; + } + JSObject::put(exec, propertyName, value, slot); +} + +void JSByteArray::put(ExecState* exec, unsigned propertyName, JSValue value) +{ + setIndex(exec, propertyName, value); +} + +void JSByteArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + unsigned length = m_storage->length(); + for (unsigned i = 0; i < length; ++i) + propertyNames.add(Identifier::from(exec, i)); + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +} + diff --git a/Source/JavaScriptCore/runtime/JSByteArray.h b/Source/JavaScriptCore/runtime/JSByteArray.h new file mode 100644 index 0000000..44bae2d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSByteArray.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSByteArray_h +#define JSByteArray_h + +#include "JSObject.h" + +#include <wtf/ByteArray.h> + +namespace JSC { + + class JSByteArray : public JSObject { + friend class JSGlobalData; + public: + bool canAccessIndex(unsigned i) { return i < m_storage->length(); } + JSValue getIndex(ExecState*, unsigned i) + { + ASSERT(canAccessIndex(i)); + return jsNumber(m_storage->data()[i]); + } + + void setIndex(unsigned i, int value) + { + ASSERT(canAccessIndex(i)); + m_storage->data()[i] = static_cast<unsigned char>(value); + } + + void setIndex(unsigned i, double value) + { + ASSERT(canAccessIndex(i)); + // The largest integer value that a double can represent without loss of precision + // is 2^53. long long is the smallest integral type that gives correct results + // when casting numbers larger than 2^31 from a value of type double. + m_storage->data()[i] = static_cast<unsigned char>(static_cast<long long>(value)); + } + + void setIndex(ExecState* exec, unsigned i, JSValue value) + { + double byteValue = value.toNumber(exec); + if (exec->hadException()) + return; + if (canAccessIndex(i)) + setIndex(i, byteValue); + } + + 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 getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + virtual const ClassInfo* classInfo() const { return m_classInfo; } + static const ClassInfo s_defaultInfo; + + size_t length() const { return m_storage->length(); } + + WTF::ByteArray* storage() const { return m_storage.get(); } + +#if !ASSERT_DISABLED + virtual ~JSByteArray(); +#endif + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + + private: + enum VPtrStealingHackType { VPtrStealingHack }; + JSByteArray(VPtrStealingHackType) + : JSObject(createStructure(jsNull())) + , m_classInfo(0) + { + } + + RefPtr<WTF::ByteArray> m_storage; + const ClassInfo* m_classInfo; + }; + + JSByteArray* asByteArray(JSValue value); + inline JSByteArray* asByteArray(JSValue value) + { + return static_cast<JSByteArray*>(value.asCell()); + } + + inline bool isJSByteArray(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsByteArrayVPtr; } + +} // namespace JSC + +#endif // JSByteArray_h diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp new file mode 100644 index 0000000..0cc1ab1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCell.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008 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 + * 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 "JSCell.h" + +#include "JSFunction.h" +#include "JSString.h" +#include "JSObject.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +#if defined NAN && defined INFINITY + +extern const double NaN = NAN; +extern const double Inf = INFINITY; + +#else // !(defined NAN && defined INFINITY) + +// The trick is to define the NaN and Inf globals with a different type than the declaration. +// This trick works because the mangled name of the globals does not include the type, although +// I'm not sure that's guaranteed. There could be alignment issues with this, since arrays of +// characters don't necessarily need the same alignment doubles do, but for now it seems to work. +// It would be good to figure out a 100% clean way that still avoids code that runs at init time. + +// Note, we have to use union to ensure alignment. Otherwise, NaN_Bytes can start anywhere, +// while NaN_double has to be 4-byte aligned for 32-bits. +// With -fstrict-aliasing enabled, unions are the only safe way to do type masquerading. + +static const union { + struct { + unsigned char NaN_Bytes[8]; + unsigned char Inf_Bytes[8]; + } bytes; + + struct { + double NaN_Double; + double Inf_Double; + } doubles; + +} NaNInf = { { +#if CPU(BIG_ENDIAN) + { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 }, + { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } +#elif CPU(MIDDLE_ENDIAN) + { 0, 0, 0xf8, 0x7f, 0, 0, 0, 0 }, + { 0, 0, 0xf0, 0x7f, 0, 0, 0, 0 } +#else + { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f }, + { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } +#endif +} } ; + +extern const double NaN = NaNInf.doubles.NaN_Double; +extern const double Inf = NaNInf.doubles.Inf_Double; + +#endif // !(defined NAN && defined INFINITY) + +bool JSCell::getUInt32(uint32_t&) const +{ + return false; +} + +bool JSCell::getString(ExecState* exec, UString&stringValue) const +{ + if (!isString()) + return false; + stringValue = static_cast<const JSString*>(this)->value(exec); + return true; +} + +UString JSCell::getString(ExecState* exec) const +{ + return isString() ? static_cast<const JSString*>(this)->value(exec) : UString(); +} + +JSObject* JSCell::getObject() +{ + return isObject() ? asObject(this) : 0; +} + +const JSObject* JSCell::getObject() const +{ + return isObject() ? static_cast<const JSObject*>(this) : 0; +} + +CallType JSCell::getCallData(CallData&) +{ + return CallTypeNone; +} + +ConstructType JSCell::getConstructData(ConstructData&) +{ + return ConstructTypeNone; +} + +bool JSCell::getOwnPropertySlot(ExecState* exec, const Identifier& identifier, PropertySlot& slot) +{ + // This is not a general purpose implementation of getOwnPropertySlot. + // It should only be called by JSValue::get. + // It calls getPropertySlot, not getOwnPropertySlot. + JSObject* object = toObject(exec); + slot.setBase(object); + if (!object->getPropertySlot(exec, identifier, slot)) + slot.setUndefined(); + return true; +} + +bool JSCell::getOwnPropertySlot(ExecState* exec, unsigned identifier, PropertySlot& slot) +{ + // This is not a general purpose implementation of getOwnPropertySlot. + // It should only be called by JSValue::get. + // It calls getPropertySlot, not getOwnPropertySlot. + JSObject* object = toObject(exec); + slot.setBase(object); + if (!object->getPropertySlot(exec, identifier, slot)) + slot.setUndefined(); + return true; +} + +void JSCell::put(ExecState* exec, const Identifier& identifier, JSValue value, PutPropertySlot& slot) +{ + toObject(exec)->put(exec, identifier, value, slot); +} + +void JSCell::put(ExecState* exec, unsigned identifier, JSValue value) +{ + toObject(exec)->put(exec, identifier, value); +} + +bool JSCell::deleteProperty(ExecState* exec, const Identifier& identifier) +{ + return toObject(exec)->deleteProperty(exec, identifier); +} + +bool JSCell::deleteProperty(ExecState* exec, unsigned identifier) +{ + return toObject(exec)->deleteProperty(exec, identifier); +} + +JSObject* JSCell::toThisObject(ExecState* exec) const +{ + return toObject(exec); +} + +const ClassInfo* JSCell::classInfo() const +{ + return 0; +} + +JSValue JSCell::getJSNumber() +{ + return JSValue(); +} + +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/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h new file mode 100644 index 0000000..7d4929d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -0,0 +1,376 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 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 + * 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. + * + */ + +#ifndef JSCell_h +#define JSCell_h + +#include "CallData.h" +#include "ConstructData.h" +#include "Collector.h" +#include "JSImmediate.h" +#include "JSValue.h" +#include "MarkStack.h" +#include "Structure.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +#if COMPILER(MSVC) + // If WTF_MAKE_NONCOPYABLE is applied to JSCell we end up with a bunch of + // undefined references to the JSCell copy constructor and assignment operator + // when linking JavaScriptCore. + class MSVCBugWorkaround { + WTF_MAKE_NONCOPYABLE(MSVCBugWorkaround); + + protected: + MSVCBugWorkaround() { } + ~MSVCBugWorkaround() { } + }; + + class JSCell : MSVCBugWorkaround { +#else + class JSCell { + WTF_MAKE_NONCOPYABLE(JSCell); +#endif + + friend class GetterSetter; + friend class Heap; + friend class JIT; + friend class JSNumberCell; + friend class JSObject; + friend class JSPropertyNameIterator; + friend class JSString; + friend class JSValue; + friend class JSAPIValueWrapper; + friend class JSZombie; + friend class JSGlobalData; + + private: + explicit JSCell(Structure*); + virtual ~JSCell(); + + public: + static PassRefPtr<Structure> createDummyStructure() + { + return Structure::create(jsNull(), TypeInfo(UnspecifiedType), AnonymousSlotCount); + } + + // Querying the type. + bool isString() const; + bool isObject() const; + virtual bool isGetterSetter() const; + bool inherits(const ClassInfo*) const; + virtual bool isAPIValueWrapper() const { return false; } + virtual bool isPropertyNameIterator() const { return false; } + + Structure* structure() const; + + // Extracting the value. + bool getString(ExecState* exec, UString&) const; + UString getString(ExecState* exec) const; // null string if not a string + JSObject* getObject(); // NULL if not an object + const JSObject* getObject() const; // NULL if not an object + + virtual CallType getCallData(CallData&); + virtual ConstructType getConstructData(ConstructData&); + + // Extracting integer values. + // FIXME: remove these methods, can check isNumberCell in JSValue && then call asNumberCell::*. + virtual bool getUInt32(uint32_t&) const; + + // Basic conversions. + 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; } + + virtual void markChildren(MarkStack&); +#if ENABLE(JSC_ZOMBIES) + virtual bool isZombie() const { return false; } +#endif + + // Object operations, with the toObject operation included. + virtual const ClassInfo* classInfo() const; + virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + virtual void put(ExecState*, unsigned propertyName, JSValue); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual bool deleteProperty(ExecState*, unsigned propertyName); + + virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue getJSNumber(); + void* vptr() { return *reinterpret_cast<void**>(this); } + void setVPtr(void* vptr) { *reinterpret_cast<void**>(this) = vptr; } + + // FIXME: Rename getOwnPropertySlot to virtualGetOwnPropertySlot, and + // fastGetOwnPropertySlot to getOwnPropertySlot. Callers should always + // call this function, not its slower virtual counterpart. (For integer + // property names, we want a similar interface with appropriate optimizations.) + bool fastGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + + protected: + static const unsigned AnonymousSlotCount = 0; + + private: + // Base implementation; for non-object classes implements getPropertySlot. + virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + + Structure* m_structure; + }; + + inline JSCell::JSCell(Structure* structure) + : m_structure(structure) + { + } + + inline JSCell::~JSCell() + { + } + + inline bool JSCell::isObject() const + { + return m_structure->typeInfo().type() == ObjectType; + } + + inline bool JSCell::isString() const + { + return m_structure->typeInfo().type() == StringType; + } + + inline Structure* JSCell::structure() const + { + return m_structure; + } + + inline void JSCell::markChildren(MarkStack&) + { + } + + inline void* JSCell::operator new(size_t size, JSGlobalData* globalData) + { + return globalData->heap.allocate(size); + } + + inline void* JSCell::operator new(size_t size, ExecState* exec) + { + return exec->heap()->allocate(size); + } + + // --- JSValue inlines ---------------------------- + + inline bool JSValue::isString() const + { + return isCell() && asCell()->isString(); + } + + inline bool JSValue::isGetterSetter() const + { + return isCell() && asCell()->isGetterSetter(); + } + + inline bool JSValue::isObject() const + { + return isCell() && asCell()->isObject(); + } + + inline bool JSValue::getString(ExecState* exec, UString& s) const + { + return isCell() && asCell()->getString(exec, s); + } + + inline UString JSValue::getString(ExecState* exec) const + { + return isCell() ? asCell()->getString(exec) : UString(); + } + + inline JSObject* JSValue::getObject() const + { + return isCell() ? asCell()->getObject() : 0; + } + + inline CallType getCallData(JSValue value, CallData& callData) + { + CallType result = value.isCell() ? value.asCell()->getCallData(callData) : CallTypeNone; + ASSERT(result == CallTypeNone || value.isValidCallee()); + return result; + } + + inline ConstructType getConstructData(JSValue value, ConstructData& constructData) + { + ConstructType result = value.isCell() ? value.asCell()->getConstructData(constructData) : ConstructTypeNone; + ASSERT(result == ConstructTypeNone || value.isValidCallee()); + return result; + } + + ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const + { + if (isInt32()) { + int32_t i = asInt32(); + v = static_cast<uint32_t>(i); + return i >= 0; + } + if (isDouble()) { + double d = asDouble(); + v = static_cast<uint32_t>(d); + return v == d; + } + return false; + } + +#if USE(JSVALUE64) + ALWAYS_INLINE JSCell* JSValue::asCell() const + { + ASSERT(isCell()); + return m_ptr; + } +#endif // USE(JSVALUE64) + + inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const + { + return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue(); + } + + inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value) + { + if (isInt32()) { + number = asInt32(); + value = *this; + return true; + } + if (isDouble()) { + number = asDouble(); + value = *this; + return true; + } + if (isCell()) + return asCell()->getPrimitiveNumber(exec, number, value); + if (isTrue()) { + number = 1.0; + value = *this; + return true; + } + if (isFalse() || isNull()) { + number = 0.0; + value = *this; + return true; + } + ASSERT(isUndefined()); + number = nonInlineNaN(); + value = *this; + return true; + } + + inline bool JSValue::toBoolean(ExecState* exec) const + { + if (isInt32()) + return asInt32() != 0; + if (isDouble()) + return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN + if (isCell()) + return asCell()->toBoolean(exec); + return isTrue(); // false, null, and undefined all convert to false. + } + + ALWAYS_INLINE double JSValue::toNumber(ExecState* exec) const + { + if (isInt32()) + return asInt32(); + if (isDouble()) + return asDouble(); + if (isCell()) + return asCell()->toNumber(exec); + if (isTrue()) + return 1.0; + return isUndefined() ? nonInlineNaN() : 0; // null and false both convert to 0. + } + + inline bool JSValue::needsThisConversion() const + { + if (UNLIKELY(!isCell())) + return true; + return asCell()->structure()->typeInfo().needsThisConversion(); + } + + inline JSValue JSValue::getJSNumber() + { + if (isInt32() || isDouble()) + return *this; + if (isCell()) + return asCell()->getJSNumber(); + return JSValue(); + } + + inline JSObject* JSValue::toObject(ExecState* exec) const + { + return isCell() ? asCell()->toObject(exec) : toObjectSlowCase(exec); + } + + inline JSObject* JSValue::toThisObject(ExecState* exec) const + { + return isCell() ? asCell()->toThisObject(exec) : toThisObjectSlowCase(exec); + } + + ALWAYS_INLINE void MarkStack::append(JSCell* cell) + { + ASSERT(!m_isCheckingForDefaultMarkViolation); + ASSERT(cell); + if (Heap::checkMarkCell(cell)) + return; + if (cell->structure()->typeInfo().type() >= CompoundType) + m_values.append(cell); + } + + ALWAYS_INLINE void MarkStack::append(JSValue value) + { + ASSERT(value); + if (value.isCell()) + append(value.asCell()); + } + + 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; + } + +#if ENABLE(JSC_ZOMBIES) + inline bool JSValue::isZombie() const + { + return isCell() && asCell() && asCell()->isZombie(); + } +#endif +} // namespace JSC + +#endif // JSCell_h diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp new file mode 100644 index 0000000..99f8e6f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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 "JSFunction.h" + +#include "CodeBlock.h" +#include "CommonIdentifiers.h" +#include "CallFrame.h" +#include "ExceptionHelpers.h" +#include "FunctionPrototype.h" +#include "JSGlobalObject.h" +#include "JSNotAnObject.h" +#include "Interpreter.h" +#include "ObjectPrototype.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "ScopeChainMark.h" + +using namespace WTF; +using namespace Unicode; + +namespace JSC { +#if ENABLE(JIT) +EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) +{ + return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); +} +#endif + +ASSERT_CLASS_FITS_IN_CELL(JSFunction); + +const ClassInfo JSFunction::info = { "Function", 0, 0, 0 }; + +bool JSFunction::isHostFunctionNonInline() const +{ + return isHostFunction(); +} + +JSFunction::JSFunction(NonNullPassRefPtr<Structure> structure) + : Base(structure) + , m_executable(adoptRef(new VPtrHackExecutable())) + , m_scopeChain(NoScopeChain()) +{ +} + +#if ENABLE(JIT) +JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, PassRefPtr<NativeExecutable> thunk) + : Base(globalObject, structure) + , m_executable(thunk) + , m_scopeChain(globalObject->globalScopeChain()) +{ + putDirect(exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); +} +#endif + +JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func) + : Base(globalObject, structure) +#if ENABLE(JIT) + , m_executable(exec->globalData().getHostFunction(func)) +#endif + , m_scopeChain(globalObject->globalScopeChain()) +{ + putDirect(exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); +#if ENABLE(JIT) + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); +#else + UNUSED_PARAM(length); + UNUSED_PARAM(func); + ASSERT_NOT_REACHED(); +#endif +} + +JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<FunctionExecutable> executable, ScopeChainNode* scopeChainNode) + : Base(scopeChainNode->globalObject, scopeChainNode->globalObject->functionStructure()) + , m_executable(executable) + , m_scopeChain(scopeChainNode) +{ + const Identifier& name = static_cast<FunctionExecutable*>(m_executable.get())->name(); + putDirect(exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); +} + +JSFunction::~JSFunction() +{ + ASSERT(vptr() == JSGlobalData::jsFunctionVPtr); + + // 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) + ASSERT(m_executable); + if (jsExecutable()->isGeneratedForCall()) + jsExecutable()->generatedBytecodeForCall().unlinkCallers(); + if (jsExecutable()->isGeneratedForConstruct()) + jsExecutable()->generatedBytecodeForConstruct().unlinkCallers(); +#endif + } +} + +static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function"; +static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function"; + +static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message) +{ + JSValue thrower = createTypeErrorFunction(exec, message); + descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); +} + +const UString& JSFunction::name(ExecState* exec) +{ + return asString(getDirect(exec->globalData().propertyNames->name))->tryGetValue(); +} + +const UString JSFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->globalData().propertyNames->displayName); + + if (displayName && isJSString(&exec->globalData(), displayName)) + return asString(displayName)->tryGetValue(); + + return UString(); +} + +const UString JSFunction::calculatedDisplayName(ExecState* exec) +{ + const UString explicitName = displayName(exec); + + if (!explicitName.isEmpty()) + return explicitName; + + return name(exec); +} + +void JSFunction::markChildren(MarkStack& markStack) +{ + Base::markChildren(markStack); + if (!isHostFunction()) { + jsExecutable()->markAggregate(markStack); + scope().markAggregate(markStack); + } +} + +CallType JSFunction::getCallData(CallData& callData) +{ +#if ENABLE(JIT) + if (isHostFunction()) { + callData.native.function = nativeFunction(); + return CallTypeHost; + } +#endif + callData.js.functionExecutable = jsExecutable(); + callData.js.scopeChain = scope().node(); + return CallTypeJS; +} + +JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&) +{ + JSFunction* thisObj = asFunction(slotBase); + ASSERT(!thisObj->isHostFunction()); + return exec->interpreter()->retrieveArguments(exec, thisObj); +} + +JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&) +{ + JSFunction* thisObj = asFunction(slotBase); + ASSERT(!thisObj->isHostFunction()); + return exec->interpreter()->retrieveCaller(exec, thisObj); +} + +JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) +{ + JSFunction* thisObj = asFunction(slotBase); + ASSERT(!thisObj->isHostFunction()); + return jsNumber(thisObj->jsExecutable()->parameterCount()); +} + +bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (isHostFunction()) + return Base::getOwnPropertySlot(exec, propertyName, slot); + + if (propertyName == exec->propertyNames().prototype) { + JSValue* location = getDirectLocation(propertyName); + + if (!location) { + JSObject* prototype = new (exec) JSObject(scope().globalObject()->emptyObjectStructure()); + prototype->putDirect(exec->propertyNames().constructor, this, DontEnum); + putDirect(exec->propertyNames().prototype, prototype, DontDelete | DontEnum); + location = getDirectLocation(propertyName); + } + + slot.setValueSlot(this, location, offsetForLocation(location)); + } + + if (propertyName == exec->propertyNames().arguments) { + if (jsExecutable()->isStrictMode()) { + throwTypeError(exec, "Can't access arguments object of a strict mode function"); + slot.setValue(jsNull()); + return true; + } + + slot.setCacheableCustom(this, argumentsGetter); + return true; + } + + if (propertyName == exec->propertyNames().length) { + slot.setCacheableCustom(this, lengthGetter); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + if (jsExecutable()->isStrictMode()) { + throwTypeError(exec, StrictModeCallerAccessError); + slot.setValue(jsNull()); + return true; + } + slot.setCacheableCustom(this, callerGetter); + return true; + } + + 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) { + if (jsExecutable()->isStrictMode()) + createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError); + else + descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + if (jsExecutable()->isStrictMode()) + createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError); + else + descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete); + return true; + } + + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (!isHostFunction() && (mode == IncludeDontEnumProperties)) { + // Make sure prototype has been reified. + PropertySlot slot; + getOwnPropertySlot(exec, exec->propertyNames().prototype, slot); + + propertyNames.add(exec->propertyNames().arguments); + propertyNames.add(exec->propertyNames().callee); + propertyNames.add(exec->propertyNames().caller); + propertyNames.add(exec->propertyNames().length); + } + Base::getOwnPropertyNames(exec, propertyNames, mode); +} + +void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + if (isHostFunction()) { + Base::put(exec, propertyName, value, slot); + return; + } + if (propertyName == exec->propertyNames().prototype) { + // Make sure prototype has been reified, such that it can only be overwritten + // following the rules set out in ECMA-262 8.12.9. + PropertySlot slot; + getOwnPropertySlot(exec, propertyName, slot); + } + if (jsExecutable()->isStrictMode()) { + if (propertyName == exec->propertyNames().arguments) { + throwTypeError(exec, StrictModeArgumentsAccessError); + return; + } + if (propertyName == exec->propertyNames().caller) { + throwTypeError(exec, StrictModeCallerAccessError); + return; + } + } + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + return; + Base::put(exec, propertyName, value, slot); +} + +bool JSFunction::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (isHostFunction()) + return Base::deleteProperty(exec, propertyName); + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + return false; + return Base::deleteProperty(exec, propertyName); +} + +// ECMA 13.2.2 [[Construct]] +ConstructType JSFunction::getConstructData(ConstructData& constructData) +{ + if (isHostFunction()) + return ConstructTypeNone; + constructData.js.functionExecutable = jsExecutable(); + constructData.js.scopeChain = scope().node(); + return ConstructTypeJS; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h new file mode 100644 index 0000000..3a2fe30 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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. + * + */ + +#ifndef JSFunction_h +#define JSFunction_h + +#include "JSObjectWithGlobalObject.h" + +namespace JSC { + + class ExecutableBase; + class FunctionExecutable; + class FunctionPrototype; + class JSActivation; + class JSGlobalObject; + class NativeExecutable; + + EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState*); + + class JSFunction : public JSObjectWithGlobalObject { + friend class JIT; + friend class JSGlobalData; + + typedef JSObjectWithGlobalObject Base; + + public: + JSFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); +#if ENABLE(JIT) + JSFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int length, const Identifier&, PassRefPtr<NativeExecutable>); +#endif + JSFunction(ExecState*, NonNullPassRefPtr<FunctionExecutable>, ScopeChainNode*); + virtual ~JSFunction(); + + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); + + ScopeChain& scope() + { + ASSERT(!isHostFunctionNonInline()); + return m_scopeChain; + } + void setScope(const ScopeChain& scopeChain) + { + ASSERT(!isHostFunctionNonInline()); + m_scopeChain = scopeChain; + } + + ExecutableBase* executable() const { return m_executable.get(); } + + // To call either of these methods include Executable.h + inline bool isHostFunction() const; + FunctionExecutable* jsExecutable() const; + + static JS_EXPORTDATA const ClassInfo info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + NativeFunction nativeFunction(); + + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + + protected: + const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesMarkChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + private: + JSFunction(NonNullPassRefPtr<Structure>); + + bool isHostFunctionNonInline() const; + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + 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*, JSValue, const Identifier&); + static JSValue callerGetter(ExecState*, JSValue, const Identifier&); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + + RefPtr<ExecutableBase> m_executable; + ScopeChain m_scopeChain; + }; + + JSFunction* asFunction(JSValue); + + inline JSFunction* asFunction(JSValue value) + { + ASSERT(asObject(value)->inherits(&JSFunction::info)); + return static_cast<JSFunction*>(asObject(value)); + } + +} // namespace JSC + +#endif // JSFunction_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp new file mode 100644 index 0000000..aca995a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JSGlobalData.h" + +#include "ArgList.h" +#include "Collector.h" +#include "CollectorHeapIterator.h" +#include "CommonIdentifiers.h" +#include "FunctionConstructor.h" +#include "GetterSetter.h" +#include "Interpreter.h" +#include "JSActivation.h" +#include "JSAPIValueWrapper.h" +#include "JSArray.h" +#include "JSByteArray.h" +#include "JSClassRef.h" +#include "JSFunction.h" +#include "JSLock.h" +#include "JSNotAnObject.h" +#include "JSPropertyNameIterator.h" +#include "JSStaticScopeObject.h" +#include "Lexer.h" +#include "Lookup.h" +#include "Nodes.h" +#include "Parser.h" +#include "RegExpCache.h" +#include "StrictEvalActivation.h" +#include <wtf/WTFThreadData.h> +#if ENABLE(REGEXP_TRACING) +#include "RegExp.h" +#endif + + +#if ENABLE(JSC_MULTIPLE_THREADS) +#include <wtf/Threading.h> +#endif + +#if PLATFORM(MAC) +#include "ProfilerServer.h" +#include <CoreFoundation/CoreFoundation.h> +#endif + +using namespace WTF; + +namespace JSC { + +extern JSC_CONST_HASHTABLE HashTable arrayTable; +extern JSC_CONST_HASHTABLE HashTable jsonTable; +extern JSC_CONST_HASHTABLE HashTable dateTable; +extern JSC_CONST_HASHTABLE HashTable mathTable; +extern JSC_CONST_HASHTABLE HashTable numberTable; +extern JSC_CONST_HASHTABLE HashTable regExpTable; +extern JSC_CONST_HASHTABLE HashTable regExpConstructorTable; +extern JSC_CONST_HASHTABLE HashTable stringTable; + +void* JSGlobalData::jsArrayVPtr; +void* JSGlobalData::jsByteArrayVPtr; +void* JSGlobalData::jsStringVPtr; +void* JSGlobalData::jsFunctionVPtr; + +void JSGlobalData::storeVPtrs() +{ + CollectorCell cell; + void* storage = &cell; + + COMPILE_ASSERT(sizeof(JSArray) <= sizeof(CollectorCell), sizeof_JSArray_must_be_less_than_CollectorCell); + JSCell* jsArray = new (storage) JSArray(JSArray::VPtrStealingHack); + JSGlobalData::jsArrayVPtr = jsArray->vptr(); + jsArray->~JSCell(); + + COMPILE_ASSERT(sizeof(JSByteArray) <= sizeof(CollectorCell), sizeof_JSByteArray_must_be_less_than_CollectorCell); + JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack); + JSGlobalData::jsByteArrayVPtr = jsByteArray->vptr(); + jsByteArray->~JSCell(); + + COMPILE_ASSERT(sizeof(JSString) <= sizeof(CollectorCell), sizeof_JSString_must_be_less_than_CollectorCell); + JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack); + JSGlobalData::jsStringVPtr = jsString->vptr(); + jsString->~JSCell(); + + COMPILE_ASSERT(sizeof(JSFunction) <= sizeof(CollectorCell), sizeof_JSFunction_must_be_less_than_CollectorCell); + JSCell* jsFunction = new (storage) JSFunction(JSFunction::createStructure(jsNull())); + JSGlobalData::jsFunctionVPtr = jsFunction->vptr(); + jsFunction->~JSCell(); +} + +JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType) + : globalDataType(globalDataType) + , clientData(0) + , arrayTable(fastNew<HashTable>(JSC::arrayTable)) + , dateTable(fastNew<HashTable>(JSC::dateTable)) + , jsonTable(fastNew<HashTable>(JSC::jsonTable)) + , mathTable(fastNew<HashTable>(JSC::mathTable)) + , numberTable(fastNew<HashTable>(JSC::numberTable)) + , regExpTable(fastNew<HashTable>(JSC::regExpTable)) + , regExpConstructorTable(fastNew<HashTable>(JSC::regExpConstructorTable)) + , stringTable(fastNew<HashTable>(JSC::stringTable)) + , activationStructure(JSActivation::createStructure(jsNull())) + , interruptedExecutionErrorStructure(JSObject::createStructure(jsNull())) + , terminatedExecutionErrorStructure(JSObject::createStructure(jsNull())) + , staticScopeStructure(JSStaticScopeObject::createStructure(jsNull())) + , strictEvalActivationStructure(StrictEvalActivation::createStructure(jsNull())) + , stringStructure(JSString::createStructure(jsNull())) + , notAnObjectStructure(JSNotAnObject::createStructure(jsNull())) + , propertyNameIteratorStructure(JSPropertyNameIterator::createStructure(jsNull())) + , getterSetterStructure(GetterSetter::createStructure(jsNull())) + , apiWrapperStructure(JSAPIValueWrapper::createStructure(jsNull())) + , dummyMarkableCellStructure(JSCell::createDummyStructure()) + , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) + , propertyNames(new CommonIdentifiers(this)) + , emptyList(new MarkedArgumentBuffer) + , lexer(new Lexer(this)) + , parser(new Parser) + , interpreter(new Interpreter) + , heap(this) + , head(0) + , dynamicGlobalObject(0) + , firstStringifierToMark(0) + , markStack(jsArrayVPtr) + , cachedUTCOffset(NaN) + , maxReentryDepth(threadStackType == ThreadStackTypeSmall ? MaxSmallThreadReentryDepth : MaxLargeThreadReentryDepth) + , m_regExpCache(new RegExpCache(this)) +#if ENABLE(REGEXP_TRACING) + , m_rtTraceList(new RTTraceList()) +#endif +#ifndef NDEBUG + , exclusiveThread(0) +#endif +{ + if (globalDataType == Default) + m_stack = wtfThreadData().stack(); + +#if PLATFORM(MAC) + startProfilerServerIfNeeded(); +#endif +#if ENABLE(JIT) && ENABLE(INTERPRETER) +#if PLATFORM(CF) + CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); + CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); + if (canUseJIT) { + m_canUseJIT = kCFBooleanTrue == canUseJIT; + CFRelease(canUseJIT); + } else + m_canUseJIT = !getenv("JavaScriptCoreUseJIT"); + CFRelease(canUseJITKey); +#elif OS(UNIX) + m_canUseJIT = !getenv("JavaScriptCoreUseJIT"); +#else + m_canUseJIT = true; +#endif +#endif +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (m_canUseJIT) + m_canUseJIT = executableAllocator.isValid(); +#endif + jitStubs = new JITThunks(this); +#endif +} + +JSGlobalData::~JSGlobalData() +{ + // By the time this is destroyed, heap.destroy() must already have been called. + + delete interpreter; +#ifndef NDEBUG + // Zeroing out to make the behavior more predictable when someone attempts to use a deleted instance. + interpreter = 0; +#endif + + arrayTable->deleteTable(); + dateTable->deleteTable(); + jsonTable->deleteTable(); + mathTable->deleteTable(); + numberTable->deleteTable(); + regExpTable->deleteTable(); + regExpConstructorTable->deleteTable(); + stringTable->deleteTable(); + + fastDelete(const_cast<HashTable*>(arrayTable)); + fastDelete(const_cast<HashTable*>(dateTable)); + fastDelete(const_cast<HashTable*>(jsonTable)); + fastDelete(const_cast<HashTable*>(mathTable)); + fastDelete(const_cast<HashTable*>(numberTable)); + fastDelete(const_cast<HashTable*>(regExpTable)); + fastDelete(const_cast<HashTable*>(regExpConstructorTable)); + fastDelete(const_cast<HashTable*>(stringTable)); + + delete parser; + delete lexer; + + deleteAllValues(opaqueJSClassData); + + delete emptyList; + + delete propertyNames; + if (globalDataType != Default) + deleteIdentifierTable(identifierTable); + + delete clientData; + delete m_regExpCache; +#if ENABLE(REGEXP_TRACING) + delete m_rtTraceList; +#endif +} + +PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type) +{ + return adoptRef(new JSGlobalData(APIContextGroup, type)); +} + +PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type) +{ + return adoptRef(new JSGlobalData(Default, type)); +} + +PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type) +{ + Structure::startIgnoringLeaks(); + RefPtr<JSGlobalData> data = create(type); + Structure::stopIgnoringLeaks(); + return data.release(); +} + +bool JSGlobalData::sharedInstanceExists() +{ + return sharedInstanceInternal(); +} + +JSGlobalData& JSGlobalData::sharedInstance() +{ + JSGlobalData*& instance = sharedInstanceInternal(); + if (!instance) { + instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef(); +#if ENABLE(JSC_MULTIPLE_THREADS) + instance->makeUsableFromMultipleThreads(); +#endif + } + return *instance; +} + +JSGlobalData*& JSGlobalData::sharedInstanceInternal() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + static JSGlobalData* sharedInstance; + return sharedInstance; +} + +#if ENABLE(JIT) +PassRefPtr<NativeExecutable> JSGlobalData::getHostFunction(NativeFunction function) +{ + return jitStubs->hostFunctionStub(this, function); +} +PassRefPtr<NativeExecutable> JSGlobalData::getHostFunction(NativeFunction function, ThunkGenerator generator) +{ + return jitStubs->hostFunctionStub(this, function, generator); +} +#endif + +JSGlobalData::ClientData::~ClientData() +{ +} + +void JSGlobalData::resetDateCache() +{ + cachedUTCOffset = NaN; + dstOffsetCache.reset(); + cachedDateString = UString(); + cachedDateStringValue = NaN; + dateInstanceCache.reset(); +} + +void JSGlobalData::startSampling() +{ + interpreter->startSampling(); +} + +void JSGlobalData::stopSampling() +{ + interpreter->stopSampling(); +} + +void JSGlobalData::dumpSampleData(ExecState* exec) +{ + interpreter->dumpSampleData(exec); +} + +void JSGlobalData::recompileAllJSFunctions() +{ + // If JavaScript is running, it's not safe to recompile, since we'll end + // up throwing away code that is live on the stack. + ASSERT(!dynamicGlobalObject); + + LiveObjectIterator it = heap.primaryHeapBegin(); + LiveObjectIterator heapEnd = heap.primaryHeapEnd(); + for ( ; it != heapEnd; ++it) { + if ((*it)->inherits(&JSFunction::info)) { + JSFunction* function = asFunction(*it); + if (!function->executable()->isHostFunction()) + function->jsExecutable()->discardCode(); + } + } +} + +#if ENABLE(REGEXP_TRACING) +void JSGlobalData::addRegExpToTrace(PassRefPtr<RegExp> regExp) +{ + m_rtTraceList->add(regExp); +} + +void JSGlobalData::dumpRegExpTrace() +{ + // The first RegExp object is ignored. It is create by the RegExpPrototype ctor and not used. + RTTraceList::iterator iter = ++m_rtTraceList->begin(); + + if (iter != m_rtTraceList->end()) { + printf("\nRegExp Tracing\n"); + printf(" match() matches\n"); + printf("Regular Expression JIT Address calls found\n"); + printf("----------------------------------------+----------------+----------+----------\n"); + + unsigned reCount = 0; + + for (; iter != m_rtTraceList->end(); ++iter, ++reCount) + (*iter)->printTraceData(); + + printf("%d Regular Expressions\n", reCount); + } + + m_rtTraceList->clear(); +} +#else +void JSGlobalData::dumpRegExpTrace() +{ +} +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h new file mode 100644 index 0000000..699f975 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -0,0 +1,269 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 JSGlobalData_h +#define JSGlobalData_h + +#include "CachedTranscendentalFunction.h" +#include "Collector.h" +#include "DateInstanceCache.h" +#include "ExecutableAllocator.h" +#include "JITStubs.h" +#include "JSValue.h" +#include "MarkStack.h" +#include "NumericStrings.h" +#include "SmallStrings.h" +#include "Terminator.h" +#include "TimeoutChecker.h" +#include "WeakRandom.h" +#include <wtf/BumpPointerAllocator.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/ThreadSpecific.h> +#include <wtf/WTFThreadData.h> +#if ENABLE(REGEXP_TRACING) +#include <wtf/ListHashSet.h> +#endif + +struct OpaqueJSClass; +struct OpaqueJSClassContextData; + +namespace JSC { + + class CodeBlock; + class CommonIdentifiers; + class IdentifierTable; + class Interpreter; + class JSGlobalObject; + class JSObject; + class Lexer; + class Parser; + class RegExpCache; + class Stringifier; + class Structure; + class UString; +#if ENABLE(REGEXP_TRACING) + class RegExp; +#endif + + struct HashTable; + struct Instruction; + + struct DSTOffsetCache { + DSTOffsetCache() + { + reset(); + } + + void reset() + { + offset = 0.0; + start = 0.0; + end = -1.0; + increment = 0.0; + } + + double offset; + double start; + double end; + double increment; + }; + + enum ThreadStackType { + ThreadStackTypeLarge, + ThreadStackTypeSmall + }; + + class JSGlobalData : public RefCounted<JSGlobalData> { + public: + // WebCore has a one-to-one mapping of threads to JSGlobalDatas; + // either create() or createLeaked() should only be called once + // on a thread, this is the 'default' JSGlobalData (it uses the + // thread's default string uniquing table from wtfThreadData). + // API contexts created using the new context group aware interface + // create APIContextGroup objects which require less locking of JSC + // than the old singleton APIShared JSGlobalData created for use by + // the original API. + enum GlobalDataType { Default, APIContextGroup, APIShared }; + + struct ClientData { + virtual ~ClientData() = 0; + }; + + bool isSharedInstance() { return globalDataType == APIShared; } + bool usingAPI() { return globalDataType != Default; } + static bool sharedInstanceExists(); + static JSGlobalData& sharedInstance(); + + static PassRefPtr<JSGlobalData> create(ThreadStackType); + static PassRefPtr<JSGlobalData> createLeaked(ThreadStackType); + static PassRefPtr<JSGlobalData> createContextGroup(ThreadStackType); + ~JSGlobalData(); + +#if ENABLE(JSC_MULTIPLE_THREADS) + // Will start tracking threads that use the heap, which is resource-heavy. + void makeUsableFromMultipleThreads() { heap.makeUsableFromMultipleThreads(); } +#endif + + GlobalDataType globalDataType; + ClientData* clientData; + + const HashTable* arrayTable; + const HashTable* dateTable; + const HashTable* jsonTable; + const HashTable* mathTable; + const HashTable* numberTable; + const HashTable* regExpTable; + const HashTable* regExpConstructorTable; + const HashTable* stringTable; + + RefPtr<Structure> activationStructure; + RefPtr<Structure> interruptedExecutionErrorStructure; + RefPtr<Structure> terminatedExecutionErrorStructure; + RefPtr<Structure> staticScopeStructure; + RefPtr<Structure> strictEvalActivationStructure; + RefPtr<Structure> stringStructure; + RefPtr<Structure> notAnObjectStructure; + RefPtr<Structure> propertyNameIteratorStructure; + RefPtr<Structure> getterSetterStructure; + RefPtr<Structure> apiWrapperStructure; + RefPtr<Structure> dummyMarkableCellStructure; + + static void storeVPtrs(); + static JS_EXPORTDATA void* jsArrayVPtr; + static JS_EXPORTDATA void* jsByteArrayVPtr; + static JS_EXPORTDATA void* jsStringVPtr; + static JS_EXPORTDATA void* jsFunctionVPtr; + + IdentifierTable* identifierTable; + 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; + DateInstanceCache dateInstanceCache; + +#if ENABLE(ASSEMBLER) + ExecutableAllocator executableAllocator; + ExecutableAllocator regexAllocator; +#endif + +#if !ENABLE(JIT) + bool canUseJIT() { return false; } // interpreter only +#elif !ENABLE(INTERPRETER) + bool canUseJIT() { return true; } // jit only +#else + bool canUseJIT() { return m_canUseJIT; } +#endif + + const StackBounds& stack() + { + return (globalDataType == Default) + ? m_stack + : wtfThreadData().stack(); + } + + Lexer* lexer; + Parser* parser; + Interpreter* interpreter; +#if ENABLE(JIT) + OwnPtr<JITThunks> jitStubs; + MacroAssemblerCodePtr getCTIStub(ThunkGenerator generator) + { + return jitStubs->ctiStub(this, generator); + } + PassRefPtr<NativeExecutable> getHostFunction(NativeFunction function); + PassRefPtr<NativeExecutable> getHostFunction(NativeFunction function, ThunkGenerator generator); +#endif + TimeoutChecker timeoutChecker; + Terminator terminator; + Heap heap; + + JSValue exception; +#if ENABLE(JIT) + ReturnAddressPtr exceptionLocation; +#endif + + HashMap<OpaqueJSClass*, OpaqueJSClassContextData*> opaqueJSClassData; + + JSGlobalObject* head; + JSGlobalObject* dynamicGlobalObject; + + HashSet<JSObject*> arrayVisitedElements; + + Stringifier* firstStringifierToMark; + + MarkStack markStack; + + double cachedUTCOffset; + DSTOffsetCache dstOffsetCache; + + UString cachedDateString; + double cachedDateStringValue; + + int maxReentryDepth; + + RegExpCache* m_regExpCache; + + BumpPointerAllocator m_regexAllocator; + +#if ENABLE(REGEXP_TRACING) + typedef ListHashSet<RefPtr<RegExp> > RTTraceList; + RTTraceList* m_rtTraceList; +#endif + +#ifndef NDEBUG + ThreadIdentifier exclusiveThread; +#endif + + CachedTranscendentalFunction<sin> cachedSin; + + void resetDateCache(); + + void startSampling(); + void stopSampling(); + void dumpSampleData(ExecState* exec); + void recompileAllJSFunctions(); + RegExpCache* regExpCache() { return m_regExpCache; } +#if ENABLE(REGEXP_TRACING) + void addRegExpToTrace(PassRefPtr<RegExp> regExp); +#endif + void dumpRegExpTrace(); + private: + JSGlobalData(GlobalDataType, ThreadStackType); + static JSGlobalData*& sharedInstanceInternal(); + void createNativeThunk(); +#if ENABLE(JIT) && ENABLE(INTERPRETER) + bool m_canUseJIT; +#endif + StackBounds m_stack; + }; + +} // namespace JSC + +#endif // JSGlobalData_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp new file mode 100644 index 0000000..408aea7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JSGlobalObject.h" + +#include "JSCallbackConstructor.h" +#include "JSCallbackFunction.h" +#include "JSCallbackObject.h" + +#include "Arguments.h" +#include "ArrayConstructor.h" +#include "ArrayPrototype.h" +#include "BooleanConstructor.h" +#include "BooleanPrototype.h" +#include "CodeBlock.h" +#include "DateConstructor.h" +#include "DatePrototype.h" +#include "ErrorConstructor.h" +#include "ErrorPrototype.h" +#include "FunctionConstructor.h" +#include "FunctionPrototype.h" +#include "GlobalEvalFunction.h" +#include "JSFunction.h" +#include "JSGlobalObjectFunctions.h" +#include "JSLock.h" +#include "JSONObject.h" +#include "Interpreter.h" +#include "MathObject.h" +#include "NativeErrorConstructor.h" +#include "NativeErrorPrototype.h" +#include "NumberConstructor.h" +#include "NumberPrototype.h" +#include "ObjectConstructor.h" +#include "ObjectPrototype.h" +#include "Profiler.h" +#include "PrototypeFunction.h" +#include "RegExpConstructor.h" +#include "RegExpMatchesArray.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "ScopeChainMark.h" +#include "StringConstructor.h" +#include "StringPrototype.h" +#include "Debugger.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSGlobalObject); + +// Default number of ticks before a timeout check should be done. +static const int initialTickCountThreshold = 255; + +// Preferred number of milliseconds between each timeout check +static const int preferredScriptCheckTimeInterval = 1000; + +static inline void markIfNeeded(MarkStack& markStack, JSValue v) +{ + if (v) + markStack.append(v); +} + +static inline void markIfNeeded(MarkStack& markStack, const RefPtr<Structure>& s) +{ + if (s) + markIfNeeded(markStack, s->storedPrototype()); +} + +JSGlobalObject::~JSGlobalObject() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (d()->debugger) + d()->debugger->detach(this); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (UNLIKELY(*profiler != 0)) { + (*profiler)->stopProfiling(globalExec(), UString()); + } + + d()->next->d()->prev = d()->prev; + d()->prev->d()->next = d()->next; + JSGlobalObject*& headObject = head(); + if (headObject == this) + headObject = d()->next; + if (headObject == this) + headObject = 0; + + 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(); + if (registerFile.clearGlobalObject(this)) + registerFile.setNumGlobals(0); + d()->destructor(d()); +} + +void JSGlobalObject::init(JSObject* thisValue) +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + structure()->disableSpecificFunctionTracking(); + + d()->globalData = Heap::heap(this)->globalData(); + d()->globalScopeChain = ScopeChain(this, d()->globalData.get(), this, thisValue); + + JSGlobalObject::globalExec()->init(0, 0, d()->globalScopeChain.node(), CallFrame::noCaller(), 0, 0); + + if (JSGlobalObject*& headObject = head()) { + d()->prev = headObject; + d()->next = headObject->d()->next; + headObject->d()->next->d()->prev = this; + headObject->d()->next = this; + } else + headObject = d()->next = d()->prev = this; + + d()->debugger = 0; + + d()->profileGroup = 0; + + reset(prototype()); +} + +void JSGlobalObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + if (symbolTablePut(propertyName, value)) + return; + JSVariableObject::put(exec, propertyName, value, slot); +} + +void JSGlobalObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + if (symbolTablePutWithAttributes(propertyName, value, attributes)) + return; + + JSValue valueBefore = getDirect(propertyName); + PutPropertySlot slot; + JSVariableObject::put(exec, propertyName, value, slot); + if (!valueBefore) { + JSValue valueAfter = getDirect(propertyName); + if (valueAfter) + JSObject::putWithAttributes(exec, propertyName, valueAfter, attributes); + } +} + +void JSGlobalObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes) +{ + PropertySlot slot; + if (!symbolTableGet(propertyName, slot)) + JSVariableObject::defineGetter(exec, propertyName, getterFunc, attributes); +} + +void JSGlobalObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes) +{ + PropertySlot slot; + if (!symbolTableGet(propertyName, slot)) + JSVariableObject::defineSetter(exec, propertyName, setterFunc, attributes); +} + +static inline JSObject* lastInPrototypeChain(JSObject* object) +{ + JSObject* o = object; + while (o->prototype().isObject()) + o = asObject(o->prototype()); + return o; +} + +void JSGlobalObject::reset(JSValue prototype) +{ + ExecState* exec = JSGlobalObject::globalExec(); + + // Prototypes + + d()->functionPrototype = new (exec) FunctionPrototype(exec, this, FunctionPrototype::createStructure(jsNull())); // The real prototype will be set once ObjectPrototype is created. + d()->prototypeFunctionStructure = PrototypeFunction::createStructure(d()->functionPrototype); + d()->internalFunctionStructure = InternalFunction::createStructure(d()->functionPrototype); + NativeFunctionWrapper* callFunction = 0; + NativeFunctionWrapper* applyFunction = 0; + d()->functionPrototype->addFunctionProperties(exec, this, d()->prototypeFunctionStructure.get(), &callFunction, &applyFunction); + d()->callFunction = callFunction; + d()->applyFunction = applyFunction; + d()->objectPrototype = new (exec) ObjectPrototype(exec, this, ObjectPrototype::createStructure(jsNull()), d()->prototypeFunctionStructure.get()); + d()->functionPrototype->structure()->setPrototypeWithoutTransition(d()->objectPrototype); + + d()->emptyObjectStructure = d()->objectPrototype->inheritorID(); + + d()->functionStructure = JSFunction::createStructure(d()->functionPrototype); + d()->callbackFunctionStructure = JSCallbackFunction::createStructure(d()->functionPrototype); + d()->argumentsStructure = Arguments::createStructure(d()->objectPrototype); + d()->callbackConstructorStructure = JSCallbackConstructor::createStructure(d()->objectPrototype); + d()->callbackObjectStructure = JSCallbackObject<JSObjectWithGlobalObject>::createStructure(d()->objectPrototype); + + d()->arrayPrototype = new (exec) ArrayPrototype(this, ArrayPrototype::createStructure(d()->objectPrototype)); + d()->arrayStructure = JSArray::createStructure(d()->arrayPrototype); + d()->regExpMatchesArrayStructure = RegExpMatchesArray::createStructure(d()->arrayPrototype); + + d()->stringPrototype = new (exec) StringPrototype(exec, this, StringPrototype::createStructure(d()->objectPrototype)); + d()->stringObjectStructure = StringObject::createStructure(d()->stringPrototype); + + d()->booleanPrototype = new (exec) BooleanPrototype(exec, this, BooleanPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->booleanObjectStructure = BooleanObject::createStructure(d()->booleanPrototype); + + d()->numberPrototype = new (exec) NumberPrototype(exec, this, NumberPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->numberObjectStructure = NumberObject::createStructure(d()->numberPrototype); + + d()->datePrototype = new (exec) DatePrototype(exec, this, DatePrototype::createStructure(d()->objectPrototype)); + d()->dateStructure = DateInstance::createStructure(d()->datePrototype); + + d()->regExpPrototype = new (exec) RegExpPrototype(exec, this, RegExpPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->regExpStructure = RegExpObject::createStructure(d()->regExpPrototype); + + d()->methodCallDummy = constructEmptyObject(exec); + + ErrorPrototype* errorPrototype = new (exec) ErrorPrototype(exec, this, ErrorPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->errorStructure = ErrorInstance::createStructure(errorPrototype); + + // Constructors + + JSCell* objectConstructor = new (exec) ObjectConstructor(exec, this, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype, d()->prototypeFunctionStructure.get()); + JSCell* functionConstructor = new (exec) FunctionConstructor(exec, this, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype); + JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, this, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype, d()->prototypeFunctionStructure.get()); + JSCell* stringConstructor = new (exec) StringConstructor(exec, this, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype); + JSCell* booleanConstructor = new (exec) BooleanConstructor(exec, this, BooleanConstructor::createStructure(d()->functionPrototype), d()->booleanPrototype); + JSCell* numberConstructor = new (exec) NumberConstructor(exec, this, NumberConstructor::createStructure(d()->functionPrototype), d()->numberPrototype); + JSCell* dateConstructor = new (exec) DateConstructor(exec, this, DateConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->datePrototype); + + d()->regExpConstructor = new (exec) RegExpConstructor(exec, this, RegExpConstructor::createStructure(d()->functionPrototype), d()->regExpPrototype); + + d()->errorConstructor = new (exec) ErrorConstructor(exec, this, ErrorConstructor::createStructure(d()->functionPrototype), errorPrototype); + + RefPtr<Structure> nativeErrorPrototypeStructure = NativeErrorPrototype::createStructure(errorPrototype); + RefPtr<Structure> nativeErrorStructure = NativeErrorConstructor::createStructure(d()->functionPrototype); + d()->evalErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "EvalError"); + d()->rangeErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "RangeError"); + d()->referenceErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "ReferenceError"); + d()->syntaxErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "SyntaxError"); + d()->typeErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "TypeError"); + d()->URIErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "URIError"); + + d()->objectPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, objectConstructor, DontEnum); + d()->functionPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, functionConstructor, DontEnum); + d()->arrayPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, arrayConstructor, DontEnum); + d()->booleanPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, booleanConstructor, DontEnum); + d()->stringPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, stringConstructor, DontEnum); + d()->numberPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, numberConstructor, DontEnum); + d()->datePrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, dateConstructor, DontEnum); + d()->regExpPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, d()->regExpConstructor, DontEnum); + errorPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, d()->errorConstructor, DontEnum); + + // Set global constructors + + // FIXME: These properties could be handled by a static hash table. + + putDirectFunctionWithoutTransition(Identifier(exec, "Object"), objectConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "Function"), functionConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "Array"), arrayConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "Boolean"), booleanConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "String"), stringConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "Number"), numberConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "Date"), dateConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "RegExp"), d()->regExpConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "Error"), d()->errorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "EvalError"), d()->evalErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "RangeError"), d()->rangeErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "ReferenceError"), d()->referenceErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "SyntaxError"), d()->syntaxErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "TypeError"), d()->typeErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "URIError"), d()->URIErrorConstructor, DontEnum); + + // Set global values. + GlobalPropertyInfo staticGlobals[] = { + GlobalPropertyInfo(Identifier(exec, "Math"), new (exec) MathObject(exec, this, MathObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete), + GlobalPropertyInfo(Identifier(exec, "NaN"), jsNaN(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "Infinity"), jsNumber(Inf), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "JSON"), new (exec) JSONObject(this, JSONObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete) + }; + + addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); + + // Set global functions. + + d()->evalFunction = new (exec) GlobalEvalFunction(exec, this, GlobalEvalFunction::createStructure(d()->functionPrototype), 1, exec->propertyNames().eval, globalFuncEval, this); + putDirectFunctionWithoutTransition(exec, d()->evalFunction, DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 2, Identifier(exec, "parseInt"), globalFuncParseInt), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "parseFloat"), globalFuncParseFloat), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "isNaN"), globalFuncIsNaN), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "isFinite"), globalFuncIsFinite), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "escape"), globalFuncEscape), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "unescape"), globalFuncUnescape), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURI"), globalFuncDecodeURI), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURIComponent"), globalFuncDecodeURIComponent), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURI"), globalFuncEncodeURI), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURIComponent"), globalFuncEncodeURIComponent), DontEnum); +#ifndef NDEBUG + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "jscprint"), globalFuncJSCPrint), DontEnum); +#endif + + resetPrototype(prototype); +} + +// Set prototype, and also insert the object prototype at the end of the chain. +void JSGlobalObject::resetPrototype(JSValue prototype) +{ + setPrototype(prototype); + + JSObject* oldLastInPrototypeChain = lastInPrototypeChain(this); + JSObject* objectPrototype = d()->objectPrototype; + if (oldLastInPrototypeChain != objectPrototype) + oldLastInPrototypeChain->setPrototype(objectPrototype); +} + +void JSGlobalObject::markChildren(MarkStack& markStack) +{ + JSVariableObject::markChildren(markStack); + + 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(); + if (registerFile.globalObject() == this) + registerFile.markGlobals(markStack, &globalData().heap); + + markIfNeeded(markStack, d()->regExpConstructor); + markIfNeeded(markStack, d()->errorConstructor); + markIfNeeded(markStack, d()->evalErrorConstructor); + markIfNeeded(markStack, d()->rangeErrorConstructor); + markIfNeeded(markStack, d()->referenceErrorConstructor); + markIfNeeded(markStack, d()->syntaxErrorConstructor); + markIfNeeded(markStack, d()->typeErrorConstructor); + markIfNeeded(markStack, d()->URIErrorConstructor); + + markIfNeeded(markStack, d()->evalFunction); + markIfNeeded(markStack, d()->callFunction); + markIfNeeded(markStack, d()->applyFunction); + + markIfNeeded(markStack, d()->objectPrototype); + markIfNeeded(markStack, d()->functionPrototype); + markIfNeeded(markStack, d()->arrayPrototype); + markIfNeeded(markStack, d()->booleanPrototype); + markIfNeeded(markStack, d()->stringPrototype); + markIfNeeded(markStack, d()->numberPrototype); + markIfNeeded(markStack, d()->datePrototype); + markIfNeeded(markStack, d()->regExpPrototype); + + markIfNeeded(markStack, d()->methodCallDummy); + + markIfNeeded(markStack, d()->errorStructure); + markIfNeeded(markStack, d()->argumentsStructure); + markIfNeeded(markStack, d()->arrayStructure); + markIfNeeded(markStack, d()->booleanObjectStructure); + markIfNeeded(markStack, d()->callbackConstructorStructure); + markIfNeeded(markStack, d()->callbackFunctionStructure); + markIfNeeded(markStack, d()->callbackObjectStructure); + markIfNeeded(markStack, d()->dateStructure); + markIfNeeded(markStack, d()->emptyObjectStructure); + markIfNeeded(markStack, d()->errorStructure); + markIfNeeded(markStack, d()->functionStructure); + markIfNeeded(markStack, d()->numberObjectStructure); + markIfNeeded(markStack, d()->prototypeFunctionStructure); + markIfNeeded(markStack, d()->regExpMatchesArrayStructure); + markIfNeeded(markStack, d()->regExpStructure); + markIfNeeded(markStack, d()->stringObjectStructure); + + // No need to mark the other structures, because their prototypes are all + // guaranteed to be referenced elsewhere. + + Register* registerArray = d()->registerArray.get(); + if (!registerArray) + return; + + size_t size = d()->registerArraySize; + markStack.appendValues(reinterpret_cast<JSValue*>(registerArray), size); +} + +ExecState* JSGlobalObject::globalExec() +{ + return CallFrame::create(d()->globalCallFrame + RegisterFile::CallFrameHeaderSize); +} + +bool JSGlobalObject::isDynamicScope(bool&) const +{ + return true; +} + +void JSGlobalObject::copyGlobalsFrom(RegisterFile& registerFile) +{ + ASSERT(!d()->registerArray); + ASSERT(!d()->registerArraySize); + + int numGlobals = registerFile.numGlobals(); + if (!numGlobals) { + d()->registers = 0; + return; + } + + Register* registerArray = copyRegisterArray(registerFile.lastGlobal(), numGlobals); + setRegisters(registerArray + numGlobals, registerArray, numGlobals); +} + +void JSGlobalObject::copyGlobalsTo(RegisterFile& registerFile) +{ + JSGlobalObject* lastGlobalObject = registerFile.globalObject(); + if (lastGlobalObject && lastGlobalObject != this) + lastGlobalObject->copyGlobalsFrom(registerFile); + + registerFile.setGlobalObject(this); + registerFile.setNumGlobals(symbolTable().size()); + + if (d()->registerArray) { + memcpy(registerFile.start() - d()->registerArraySize, d()->registerArray.get(), d()->registerArraySize * sizeof(Register)); + setRegisters(registerFile.start(), 0, 0); + } +} + +void* JSGlobalObject::operator new(size_t size, JSGlobalData* globalData) +{ + return globalData->heap.allocate(size); +} + +void JSGlobalObject::destroyJSGlobalObjectData(void* jsGlobalObjectData) +{ + delete static_cast<JSGlobalObjectData*>(jsGlobalObjectData); +} + +DynamicGlobalObjectScope::DynamicGlobalObjectScope(CallFrame* callFrame, JSGlobalObject* dynamicGlobalObject) + : m_dynamicGlobalObjectSlot(callFrame->globalData().dynamicGlobalObject) + , m_savedDynamicGlobalObject(m_dynamicGlobalObjectSlot) +{ + if (!m_dynamicGlobalObjectSlot) { +#if ENABLE(ASSEMBLER) + if (ExecutableAllocator::underMemoryPressure()) + callFrame->globalData().recompileAllJSFunctions(); +#endif + + m_dynamicGlobalObjectSlot = dynamicGlobalObject; + + // Reset the date cache between JS invocations to force the VM + // to observe time zone changes. + callFrame->globalData().resetDateCache(); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h new file mode 100644 index 0000000..a22b0aa --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * Copyright (C) 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 + * 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. + * + */ + +#ifndef JSGlobalObject_h +#define JSGlobalObject_h + +#include "JSArray.h" +#include "JSGlobalData.h" +#include "JSVariableObject.h" +#include "JSWeakObjectMapRefInternal.h" +#include "NativeFunctionWrapper.h" +#include "NumberPrototype.h" +#include "StringPrototype.h" +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/RandomNumber.h> + +namespace JSC { + + class ArrayPrototype; + class BooleanPrototype; + class DatePrototype; + class Debugger; + class ErrorConstructor; + class FunctionPrototype; + class GlobalCodeBlock; + class GlobalEvalFunction; + class NativeErrorConstructor; + class ProgramCodeBlock; + class PrototypeFunction; + class RegExpConstructor; + class RegExpPrototype; + class RegisterFile; + + struct ActivationStackNode; + struct HashTable; + + typedef Vector<ExecState*, 16> ExecStateStack; + + class JSGlobalObject : public JSVariableObject { + protected: + using JSVariableObject::JSVariableObjectData; + typedef HashSet<RefPtr<OpaqueJSWeakObjectMap> > WeakMapSet; + + struct JSGlobalObjectData : public JSVariableObjectData { + // 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) + , errorConstructor(0) + , evalErrorConstructor(0) + , rangeErrorConstructor(0) + , referenceErrorConstructor(0) + , syntaxErrorConstructor(0) + , typeErrorConstructor(0) + , URIErrorConstructor(0) + , evalFunction(0) + , callFunction(0) + , applyFunction(0) + , objectPrototype(0) + , functionPrototype(0) + , arrayPrototype(0) + , booleanPrototype(0) + , stringPrototype(0) + , numberPrototype(0) + , datePrototype(0) + , regExpPrototype(0) + , methodCallDummy(0) + , weakRandom(static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0))) + { + } + + Destructor destructor; + + size_t registerArraySize; + + JSGlobalObject* next; + JSGlobalObject* prev; + + Debugger* debugger; + + ScopeChain globalScopeChain; + Register globalCallFrame[RegisterFile::CallFrameHeaderSize]; + + RegExpConstructor* regExpConstructor; + ErrorConstructor* errorConstructor; + NativeErrorConstructor* evalErrorConstructor; + NativeErrorConstructor* rangeErrorConstructor; + NativeErrorConstructor* referenceErrorConstructor; + NativeErrorConstructor* syntaxErrorConstructor; + NativeErrorConstructor* typeErrorConstructor; + NativeErrorConstructor* URIErrorConstructor; + + GlobalEvalFunction* evalFunction; + NativeFunctionWrapper* callFunction; + NativeFunctionWrapper* applyFunction; + + ObjectPrototype* objectPrototype; + FunctionPrototype* functionPrototype; + ArrayPrototype* arrayPrototype; + BooleanPrototype* booleanPrototype; + StringPrototype* stringPrototype; + NumberPrototype* numberPrototype; + DatePrototype* datePrototype; + RegExpPrototype* regExpPrototype; + + JSObject* methodCallDummy; + + RefPtr<Structure> argumentsStructure; + RefPtr<Structure> arrayStructure; + RefPtr<Structure> booleanObjectStructure; + RefPtr<Structure> callbackConstructorStructure; + RefPtr<Structure> callbackFunctionStructure; + RefPtr<Structure> callbackObjectStructure; + RefPtr<Structure> dateStructure; + RefPtr<Structure> emptyObjectStructure; + RefPtr<Structure> errorStructure; + RefPtr<Structure> functionStructure; + RefPtr<Structure> numberObjectStructure; + RefPtr<Structure> prototypeFunctionStructure; + RefPtr<Structure> regExpMatchesArrayStructure; + RefPtr<Structure> regExpStructure; + RefPtr<Structure> stringObjectStructure; + RefPtr<Structure> internalFunctionStructure; + + SymbolTable symbolTable; + unsigned profileGroup; + + RefPtr<JSGlobalData> globalData; + + HashSet<GlobalCodeBlock*> codeBlocks; + WeakMapSet weakMaps; + WeakRandom weakRandom; + }; + + public: + void* operator new(size_t, JSGlobalData*); + + explicit JSGlobalObject() + : JSVariableObject(JSGlobalObject::createStructure(jsNull()), new JSGlobalObjectData(destroyJSGlobalObjectData)) + { + COMPILE_ASSERT(JSGlobalObject::AnonymousSlotCount == 1, JSGlobalObject_has_only_a_single_slot); + putAnonymousValue(0, this); + init(this); + } + + explicit JSGlobalObject(NonNullPassRefPtr<Structure> structure) + : JSVariableObject(structure, new JSGlobalObjectData(destroyJSGlobalObjectData)) + { + COMPILE_ASSERT(JSGlobalObject::AnonymousSlotCount == 1, JSGlobalObject_has_only_a_single_slot); + putAnonymousValue(0, this); + init(this); + } + + protected: + JSGlobalObject(NonNullPassRefPtr<Structure> structure, JSGlobalObjectData* data, JSObject* thisValue) + : JSVariableObject(structure, data) + { + COMPILE_ASSERT(JSGlobalObject::AnonymousSlotCount == 1, JSGlobalObject_has_only_a_single_slot); + putAnonymousValue(0, this); + init(thisValue); + } + + public: + virtual ~JSGlobalObject(); + + 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, 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; } + JSGlobalObject* next() { return d()->next; } + + // The following accessors return pristine values, even if a script + // replaces the global object's associated property. + + RegExpConstructor* regExpConstructor() const { return d()->regExpConstructor; } + + ErrorConstructor* errorConstructor() const { return d()->errorConstructor; } + NativeErrorConstructor* evalErrorConstructor() const { return d()->evalErrorConstructor; } + NativeErrorConstructor* rangeErrorConstructor() const { return d()->rangeErrorConstructor; } + NativeErrorConstructor* referenceErrorConstructor() const { return d()->referenceErrorConstructor; } + NativeErrorConstructor* syntaxErrorConstructor() const { return d()->syntaxErrorConstructor; } + NativeErrorConstructor* typeErrorConstructor() const { return d()->typeErrorConstructor; } + NativeErrorConstructor* URIErrorConstructor() const { return d()->URIErrorConstructor; } + + GlobalEvalFunction* evalFunction() const { return d()->evalFunction; } + + ObjectPrototype* objectPrototype() const { return d()->objectPrototype; } + FunctionPrototype* functionPrototype() const { return d()->functionPrototype; } + ArrayPrototype* arrayPrototype() const { return d()->arrayPrototype; } + BooleanPrototype* booleanPrototype() const { return d()->booleanPrototype; } + StringPrototype* stringPrototype() const { return d()->stringPrototype; } + NumberPrototype* numberPrototype() const { return d()->numberPrototype; } + DatePrototype* datePrototype() const { return d()->datePrototype; } + RegExpPrototype* regExpPrototype() const { return d()->regExpPrototype; } + + JSObject* methodCallDummy() const { return d()->methodCallDummy; } + + Structure* argumentsStructure() const { return d()->argumentsStructure.get(); } + Structure* arrayStructure() const { return d()->arrayStructure.get(); } + Structure* booleanObjectStructure() const { return d()->booleanObjectStructure.get(); } + Structure* callbackConstructorStructure() const { return d()->callbackConstructorStructure.get(); } + Structure* callbackFunctionStructure() const { return d()->callbackFunctionStructure.get(); } + Structure* callbackObjectStructure() const { return d()->callbackObjectStructure.get(); } + Structure* dateStructure() const { return d()->dateStructure.get(); } + Structure* emptyObjectStructure() const { return d()->emptyObjectStructure.get(); } + Structure* errorStructure() const { return d()->errorStructure.get(); } + Structure* functionStructure() const { return d()->functionStructure.get(); } + Structure* numberObjectStructure() const { return d()->numberObjectStructure.get(); } + Structure* prototypeFunctionStructure() const { return d()->prototypeFunctionStructure.get(); } + Structure* internalFunctionStructure() const { return d()->internalFunctionStructure.get(); } + Structure* regExpMatchesArrayStructure() const { return d()->regExpMatchesArrayStructure.get(); } + Structure* regExpStructure() const { return d()->regExpStructure.get(); } + Structure* stringObjectStructure() const { return d()->stringObjectStructure.get(); } + + void setProfileGroup(unsigned value) { d()->profileGroup = value; } + unsigned profileGroup() const { return d()->profileGroup; } + + Debugger* debugger() const { return d()->debugger; } + void setDebugger(Debugger* debugger) { d()->debugger = debugger; } + + virtual bool supportsProfiling() const { return false; } + virtual bool supportsRichSourceInfo() const { return true; } + + ScopeChain& globalScopeChain() { return d()->globalScopeChain; } + + virtual bool isGlobalObject() const { return true; } + + virtual ExecState* globalExec(); + + virtual bool shouldInterruptScript() const { return true; } + + virtual bool allowsAccessFrom(const JSGlobalObject*) const { return true; } + + virtual bool isDynamicScope(bool& requiresDynamicChecks) const; + + HashSet<GlobalCodeBlock*>& codeBlocks() { return d()->codeBlocks; } + + void copyGlobalsFrom(RegisterFile&); + void copyGlobalsTo(RegisterFile&); + + void resetPrototype(JSValue prototype); + + JSGlobalData& globalData() const { return *d()->globalData.get(); } + JSGlobalObjectData* d() const { return static_cast<JSGlobalObjectData*>(JSVariableObject::d); } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + void registerWeakMap(OpaqueJSWeakObjectMap* map) + { + d()->weakMaps.add(map); + } + + void deregisterWeakMap(OpaqueJSWeakObjectMap* map) + { + d()->weakMaps.remove(map); + } + + double weakRandomNumber() { return d()->weakRandom.get(); } + protected: + + static const unsigned AnonymousSlotCount = JSVariableObject::AnonymousSlotCount + 1; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; + + struct GlobalPropertyInfo { + GlobalPropertyInfo(const Identifier& i, JSValue v, unsigned a) + : identifier(i) + , value(v) + , attributes(a) + { + } + + const Identifier identifier; + JSValue value; + unsigned attributes; + }; + void addStaticGlobals(GlobalPropertyInfo*, int count); + + private: + static void destroyJSGlobalObjectData(void*); + + // FIXME: Fold reset into init. + void init(JSObject* thisValue); + void reset(JSValue prototype); + + void setRegisters(Register* registers, Register* registerArray, size_t count); + + void* operator new(size_t); // can only be allocated with JSGlobalData + }; + + JSGlobalObject* asGlobalObject(JSValue); + + inline JSGlobalObject* asGlobalObject(JSValue value) + { + ASSERT(asObject(value)->isGlobalObject()); + return static_cast<JSGlobalObject*>(asObject(value)); + } + + inline void JSGlobalObject::setRegisters(Register* registers, Register* registerArray, size_t count) + { + JSVariableObject::setRegisters(registers, registerArray); + d()->registerArraySize = count; + } + + inline void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count) + { + size_t oldSize = d()->registerArraySize; + size_t newSize = oldSize + count; + Register* registerArray = new Register[newSize]; + if (d()->registerArray) + memcpy(registerArray + count, d()->registerArray.get(), oldSize * sizeof(Register)); + setRegisters(registerArray + newSize, registerArray, newSize); + + for (int i = 0, index = -static_cast<int>(oldSize) - 1; i < count; ++i, --index) { + GlobalPropertyInfo& global = globals[i]; + ASSERT(global.attributes & DontDelete); + SymbolTableEntry newEntry(index, global.attributes); + symbolTable().add(global.identifier.impl(), newEntry); + registerAt(index) = global.value; + } + } + + inline bool JSGlobalObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) + { + if (JSVariableObject::getOwnPropertySlot(exec, propertyName, slot)) + return true; + 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; + if (JSVariableObject::getOwnPropertySlot(exec, propertyName, slot)) + return true; + bool slotIsWriteable; + return symbolTableGet(propertyName, slot, slotIsWriteable); + } + + inline JSValue Structure::prototypeForLookup(ExecState* exec) const + { + if (typeInfo().type() == ObjectType) + return m_prototype; + + ASSERT(typeInfo().type() == StringType); + return exec->lexicalGlobalObject()->stringPrototype(); + } + + inline StructureChain* Structure::prototypeChain(ExecState* exec) const + { + // We cache our prototype chain so our clients can share it. + if (!isValid(exec, m_cachedPrototypeChain.get())) { + JSValue prototype = prototypeForLookup(exec); + m_cachedPrototypeChain = StructureChain::create(prototype.isNull() ? 0 : asObject(prototype)->structure()); + } + return m_cachedPrototypeChain.get(); + } + + inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const + { + if (!cachedPrototypeChain) + return false; + + JSValue prototype = prototypeForLookup(exec); + RefPtr<Structure>* cachedStructure = cachedPrototypeChain->head(); + while(*cachedStructure && !prototype.isNull()) { + if (asObject(prototype)->structure() != *cachedStructure) + return false; + ++cachedStructure; + prototype = asObject(prototype)->prototype(); + } + return prototype.isNull() && !*cachedStructure; + } + + inline JSGlobalObject* ExecState::dynamicGlobalObject() + { + if (this == lexicalGlobalObject()->globalExec()) + return lexicalGlobalObject(); + + // For any ExecState that's not a globalExec, the + // dynamic global object must be set since code is running + ASSERT(globalData().dynamicGlobalObject); + return globalData().dynamicGlobalObject; + } + + inline JSObject* constructEmptyObject(ExecState* exec) + { + return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); + } + + inline JSObject* constructEmptyObject(ExecState* exec, JSGlobalObject* globalObject) + { + return new (exec) JSObject(globalObject->emptyObjectStructure()); + } + + inline JSArray* constructEmptyArray(ExecState* exec) + { + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure()); + } + + inline JSArray* constructEmptyArray(ExecState* exec, JSGlobalObject* globalObject) + { + return new (exec) JSArray(globalObject->arrayStructure()); + } + + inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength) + { + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength, CreateInitialized); + } + + 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); + + ~DynamicGlobalObjectScope() + { + m_dynamicGlobalObjectSlot = m_savedDynamicGlobalObject; + } + + private: + JSGlobalObject*& m_dynamicGlobalObjectSlot; + JSGlobalObject* m_savedDynamicGlobalObject; + }; + +} // namespace JSC + +#endif // JSGlobalObject_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp new file mode 100644 index 0000000..284806e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -0,0 +1,600 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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 "JSGlobalObjectFunctions.h" + +#include "CallFrame.h" +#include "GlobalEvalFunction.h" +#include "Interpreter.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Lexer.h" +#include "LiteralParser.h" +#include "Nodes.h" +#include "Parser.h" +#include "UStringBuilder.h" +#include "dtoa.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/unicode/UTF8.h> + +using namespace WTF; +using namespace Unicode; + +namespace JSC { + +static JSValue encode(ExecState* exec, const char* doNotEscape) +{ + UString str = exec->argument(0).toString(exec); + CString cstr = str.utf8(true); + if (!cstr.data()) + return throwError(exec, createURIError(exec, "String contained an illegal UTF-16 sequence.")); + + JSStringBuilder builder; + const char* p = cstr.data(); + for (size_t k = 0; k < cstr.length(); k++, p++) { + char c = *p; + if (c && strchr(doNotEscape, c)) + builder.append(c); + else { + char tmp[4]; + snprintf(tmp, sizeof(tmp), "%%%02X", static_cast<unsigned char>(c)); + builder.append(tmp); + } + } + return builder.build(exec); +} + +static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) +{ + JSStringBuilder builder; + UString str = exec->argument(0).toString(exec); + int k = 0; + int len = str.length(); + const UChar* d = str.characters(); + UChar u = 0; + while (k < len) { + const UChar* p = d + k; + UChar c = *p; + if (c == '%') { + int charLen = 0; + if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) { + const char b0 = Lexer::convertHex(p[1], p[2]); + const int sequenceLen = UTF8SequenceLength(b0); + if (sequenceLen != 0 && k <= len - sequenceLen * 3) { + charLen = sequenceLen * 3; + char sequence[5]; + sequence[0] = b0; + for (int i = 1; i < sequenceLen; ++i) { + const UChar* q = p + i * 3; + if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2])) + sequence[i] = Lexer::convertHex(q[1], q[2]); + else { + charLen = 0; + break; + } + } + if (charLen != 0) { + sequence[sequenceLen] = 0; + const int character = decodeUTF8Sequence(sequence); + if (character < 0 || character >= 0x110000) + charLen = 0; + else if (character >= 0x10000) { + // Convert to surrogate pair. + builder.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10))); + u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF)); + } else + u = static_cast<UChar>(character); + } + } + } + if (charLen == 0) { + if (strict) + return throwError(exec, createURIError(exec, "URI error")); + // The only case where we don't use "strict" mode is the "unescape" function. + // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. + if (k <= len - 6 && p[1] == 'u' + && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3]) + && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) { + charLen = 6; + u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]); + } + } + if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) { + c = u; + k += charLen - 1; + } + } + k++; + builder.append(c); + } + return builder.build(exec); +} + +bool isStrWhiteSpace(UChar c) +{ + switch (c) { + // ECMA-262-5th 7.2 & 7.3 + case 0x0009: + case 0x000A: + case 0x000B: + case 0x000C: + case 0x000D: + case 0x0020: + case 0x00A0: + case 0x2028: + case 0x2029: + case 0xFEFF: + return true; + default: + return c > 0xff && isSeparatorSpace(c); + } +} + +static int parseDigit(unsigned short c, int radix) +{ + int digit = -1; + + if (c >= '0' && c <= '9') + digit = c - '0'; + else if (c >= 'A' && c <= 'Z') + digit = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + digit = c - 'a' + 10; + + if (digit >= radix) + return -1; + return digit; +} + +double parseIntOverflow(const char* s, int length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const char* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == Inf) { + if (*p != '0') { + number = Inf; + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + +double parseIntOverflow(const UChar* s, int length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const UChar* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == Inf) { + if (*p != '0') { + number = Inf; + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + +static double parseInt(const UString& s, int radix) +{ + int length = s.length(); + const UChar* data = s.characters(); + int p = 0; + + while (p < length && isStrWhiteSpace(data[p])) + ++p; + + double sign = 1; + if (p < length) { + if (data[p] == '+') + ++p; + else if (data[p] == '-') { + sign = -1; + ++p; + } + } + + if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) { + radix = 16; + p += 2; + } else if (radix == 0) { + if (p < length && data[p] == '0') + radix = 8; + else + radix = 10; + } + + if (radix < 2 || radix > 36) + return NaN; + + int firstDigitPosition = p; + bool sawDigit = false; + double number = 0; + while (p < length) { + int digit = parseDigit(data[p], radix); + if (digit == -1) + break; + sawDigit = true; + number *= radix; + number += digit; + ++p; + } + + if (number >= mantissaOverflowLowerBound) { + if (radix == 10) + number = WTF::strtod(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), 0); + else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) + number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix); + } + + if (!sawDigit) + return NaN; + + return sign * number; +} + +static const int SizeOfInfinity = 8; + +static bool isInfinity(const UChar* data, const UChar* end) +{ + return (end - data) >= SizeOfInfinity + && data[0] == 'I' + && data[1] == 'n' + && data[2] == 'f' + && data[3] == 'i' + && data[4] == 'n' + && data[5] == 'i' + && data[6] == 't' + && data[7] == 'y'; +} + +// See ecma-262 9.3.1 +static double jsHexIntegerLiteral(const UChar*& data, const UChar* end) +{ + // Hex number. + data += 2; + const UChar* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 16 + toASCIIHexValue(*data); + ++data; + if (data == end) + break; + if (!isASCIIHexDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16); + + return number; +} + +// See ecma-262 9.3.1 +static double jsStrDecimalLiteral(const UChar*& data, const UChar* end) +{ + ASSERT(data < end); + + // Copy the sting into a null-terminated byte buffer, and call strtod. + Vector<char, 32> byteBuffer; + for (const UChar* characters = data; characters < end; ++characters) { + UChar character = *characters; + byteBuffer.append(isASCII(character) ? character : 0); + } + byteBuffer.append(0); + char* endOfNumber; + double number = WTF::strtod(byteBuffer.data(), &endOfNumber); + + // Check if strtod found a number; if so return it. + ptrdiff_t consumed = endOfNumber - byteBuffer.data(); + if (consumed) { + data += consumed; + return number; + } + + // Check for [+-]?Infinity + switch (*data) { + case 'I': + if (isInfinity(data, end)) { + data += SizeOfInfinity; + return Inf; + } + break; + + case '+': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return Inf; + } + break; + + case '-': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return -Inf; + } + break; + } + + // Not a number. + return NaN; +} + +// See ecma-262 9.3.1 +double jsToNumber(const UString& s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s.characters()[0]; + if (isASCIIDigit(c)) + return c - '0'; + if (isStrWhiteSpace(c)) + return 0; + return NaN; + } + + const UChar* data = s.characters(); + const UChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return 0.0; + + double number; + if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2])) + number = jsHexIntegerLiteral(data, end); + else + number = jsStrDecimalLiteral(data, end); + + // Allow trailing white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + if (data != end) + return NaN; + + return number; +} + +static double parseFloat(const UString& s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s.characters()[0]; + if (isASCIIDigit(c)) + return c - '0'; + return NaN; + } + + const UChar* data = s.characters(); + const UChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return NaN; + + return jsStrDecimalLiteral(data, end); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) +{ + JSObject* thisObject = exec->hostThisValue().toThisObject(exec); + JSObject* unwrappedObject = thisObject->unwrappedObject(); + if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != exec->callee()) + return throwVMError(exec, createEvalError(exec, "The \"this\" value passed to eval must be the global object from which eval originated")); + + JSValue x = exec->argument(0); + if (!x.isString()) + return JSValue::encode(x); + + UString s = x.toString(exec); + + LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return JSValue::encode(parsedObject); + + RefPtr<EvalExecutable> eval = EvalExecutable::create(exec, makeSource(s), false); + JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node()); + if (error) + return throwVMError(exec, error); + + return JSValue::encode(exec->interpreter()->execute(eval.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node())); +} + +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) +{ + JSValue value = exec->argument(0); + int32_t radix = exec->argument(1).toInt32(exec); + + if (radix != 0 && radix != 10) + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); + + if (value.isInt32()) + return JSValue::encode(value); + + if (value.isDouble()) { + double d = value.asDouble(); + if (isfinite(d)) + return JSValue::encode(jsNumber((d > 0) ? floor(d) : ceil(d))); + if (isnan(d) || isinf(d)) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(0)); + } + + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) +{ + return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) +{ + return JSValue::encode(jsBoolean(isnan(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) +{ + double n = exec->argument(0).toNumber(exec); + return JSValue::encode(jsBoolean(!isnan(n) && !isinf(n))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec) +{ + static const char do_not_unescape_when_decoding_URI[] = + "#$&+,/:;=?@"; + + return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) +{ + return JSValue::encode(decode(exec, "", true)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) +{ + static const char do_not_escape_when_encoding_URI[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!#$&'()*+,-./:;=?@_~"; + + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) +{ + static const char do_not_escape_when_encoding_URI_component[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!'()*-._~"; + + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) +{ + static const char do_not_escape[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "*+-./@_"; + + JSStringBuilder builder; + UString str = exec->argument(0).toString(exec); + const UChar* c = str.characters(); + for (unsigned k = 0; k < str.length(); k++, c++) { + int u = c[0]; + if (u > 255) { + char tmp[7]; + snprintf(tmp, sizeof(tmp), "%%u%04X", u); + builder.append(tmp); + } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u))) + builder.append(c, 1); + else { + char tmp[4]; + snprintf(tmp, sizeof(tmp), "%%%02X", u); + builder.append(tmp); + } + } + + return JSValue::encode(builder.build(exec)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) +{ + UStringBuilder builder; + UString str = exec->argument(0).toString(exec); + int k = 0; + int len = str.length(); + while (k < len) { + const UChar* c = str.characters() + k; + UChar u; + if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { + if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { + u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]); + c = &u; + k += 5; + } + } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { + u = UChar(Lexer::convertHex(c[1], c[2])); + c = &u; + k += 2; + } + k++; + builder.append(*c); + } + + return JSValue::encode(jsString(exec, builder.toUString())); +} + +#ifndef NDEBUG +EncodedJSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState* exec) +{ + CString string = exec->argument(0).toString(exec).utf8(); + puts(string.data()); + return JSValue::encode(jsUndefined()); +} +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h new file mode 100644 index 0000000..6dc7343 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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. + * + */ + +#ifndef JSGlobalObjectFunctions_h +#define JSGlobalObjectFunctions_h + +#include "JSValue.h" +#include <wtf/unicode/Unicode.h> + +namespace JSC { + + class ArgList; + class ExecState; + class JSObject; + + // FIXME: These functions should really be in JSGlobalObject.cpp, but putting them there + // is a 0.5% reduction. + + EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState*); +#ifndef NDEBUG + EncodedJSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState*); +#endif + + static const double mantissaOverflowLowerBound = 9007199254740992.0; + double parseIntOverflow(const char*, int length, int radix); + double parseIntOverflow(const UChar*, int length, int radix); + bool isStrWhiteSpace(UChar); + double jsToNumber(const UString& s); + +} // namespace JSC + +#endif // JSGlobalObjectFunctions_h diff --git a/Source/JavaScriptCore/runtime/JSImmediate.cpp b/Source/JavaScriptCore/runtime/JSImmediate.cpp new file mode 100644 index 0000000..846238d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSImmediate.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2003-2006, 2008 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 + * 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 "JSImmediate.h" + +namespace JSC { + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSImmediate.h b/Source/JavaScriptCore/runtime/JSImmediate.h new file mode 100644 index 0000000..68ba75c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSImmediate.h @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * + * 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. + * + */ + +#ifndef JSImmediate_h +#define JSImmediate_h + +#if USE(JSVALUE64) + +#include <wtf/Assertions.h> +#include <wtf/AlwaysInline.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> +#include "JSValue.h" +#include <limits> +#include <limits.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdlib.h> + +namespace JSC { + + class ExecState; + class JSCell; + class JSGlobalData; + class JSObject; + class UString; + + inline intptr_t reinterpretDoubleToIntptr(double value) + { + return WTF::bitwise_cast<intptr_t>(value); + } + + inline double reinterpretIntptrToDouble(intptr_t value) + { + return WTF::bitwise_cast<double>(value); + } + + /* + * A JSValue* is either a pointer to a cell (a heap-allocated object) or an immediate (a type-tagged + * value masquerading as a pointer). The low two bits in a JSValue* are available for type tagging + * because allocator alignment guarantees they will be 00 in cell pointers. + * + * For example, on a 32 bit system: + * + * JSCell*: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 00 + * [ high 30 bits: pointer address ] [ low 2 bits -- always 0 ] + * JSImmediate: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX TT + * [ high 30 bits: 'payload' ] [ low 2 bits -- tag ] + * + * Where the bottom two bits are non-zero they either indicate that the immediate is a 31 bit signed + * integer, or they mark the value as being an immediate of a type other than integer, with a secondary + * tag used to indicate the exact type. + * + * Where the lowest bit is set (TT is equal to 01 or 11) the high 31 bits form a 31 bit signed int value. + * Where TT is equal to 10 this indicates this is a type of immediate other than an integer, and the next + * two bits will form an extended tag. + * + * 31 bit signed int: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X1 + * [ high 30 bits of the value ] [ high bit part of value ] + * Other: YYYYYYYYYYYYYYYYYYYYYYYYYYYY ZZ 10 + * [ extended 'payload' ] [ extended tag ] [ tag 'other' ] + * + * Where the first bit of the extended tag is set this flags the value as being a boolean, and the following + * bit would flag the value as undefined. If neither bits are set, the value is null. + * + * Other: YYYYYYYYYYYYYYYYYYYYYYYYYYYY UB 10 + * [ extended 'payload' ] [ undefined | bool ] [ tag 'other' ] + * + * For boolean value the lowest bit in the payload holds the value of the bool, all remaining bits are zero. + * For undefined or null immediates the payload is zero. + * + * Boolean: 000000000000000000000000000V 01 10 + * [ boolean value ] [ bool ] [ tag 'other' ] + * Undefined: 0000000000000000000000000000 10 10 + * [ zero ] [ undefined ] [ tag 'other' ] + * Null: 0000000000000000000000000000 00 10 + * [ zero ] [ zero ] [ tag 'other' ] + */ + + /* + * On 64-bit platforms, we support an alternative encoding form for immediates, if + * USE(JSVALUE64) is defined. When this format is used, double precision + * floating point values may also be encoded as JSImmediates. + * + * The encoding makes use of unused NaN space in the IEEE754 representation. Any value + * with the top 13 bits set represents a QNaN (with the sign bit set). QNaN values + * can encode a 51-bit payload. Hardware produced and C-library payloads typically + * have a payload of zero. We assume that non-zero payloads are available to encode + * pointer and integer values. Since any 64-bit bit pattern where the top 15 bits are + * all set represents a NaN with a non-zero payload, we can use this space in the NaN + * ranges to encode other values (however there are also other ranges of NaN space that + * could have been selected). This range of NaN space is represented by 64-bit numbers + * begining with the 16-bit hex patterns 0xFFFE and 0xFFFF - we rely on the fact that no + * valid double-precision numbers will begin fall in these ranges. + * + * The scheme we have implemented encodes double precision values by adding 2^48 to the + * 64-bit integer representation of the number. After this manipulation, no encoded + * double-precision value will begin with the pattern 0x0000 or 0xFFFF. + * + * The top 16-bits denote the type of the encoded JSImmediate: + * + * Pointer: 0000:PPPP:PPPP:PPPP + * 0001:****:****:**** + * Double:{ ... + * FFFE:****:****:**** + * Integer: FFFF:0000:IIII:IIII + * + * 32-bit signed integers are marked with the 16-bit tag 0xFFFF. The tag 0x0000 + * denotes a pointer, or another form of tagged immediate. Boolean, null and undefined + * values are encoded in the same manner as the default format. + */ + + class JSImmediate { + private: + friend class JIT; + friend class JSValue; + friend class JSInterfaceJIT; + friend class SpecializedThunkJIT; + friend JSValue jsNumber(ExecState* exec, double d); + friend JSValue jsNumber(ExecState*, char i); + friend JSValue jsNumber(ExecState*, unsigned char i); + friend JSValue jsNumber(ExecState*, short i); + friend JSValue jsNumber(ExecState*, unsigned short i); + friend JSValue jsNumber(ExecState* exec, int i); + friend JSValue jsNumber(ExecState* exec, unsigned i); + friend JSValue jsNumber(ExecState* exec, long i); + friend JSValue jsNumber(ExecState* exec, unsigned long i); + friend JSValue jsNumber(ExecState* exec, long long i); + friend JSValue jsNumber(ExecState* exec, unsigned long long i); + friend JSValue jsNumber(JSGlobalData* globalData, double d); + friend JSValue jsNumber(JSGlobalData* globalData, short i); + friend JSValue jsNumber(JSGlobalData* globalData, unsigned short i); + friend JSValue jsNumber(JSGlobalData* globalData, int i); + friend JSValue jsNumber(JSGlobalData* globalData, unsigned i); + friend JSValue jsNumber(JSGlobalData* globalData, long i); + friend JSValue jsNumber(JSGlobalData* globalData, unsigned long i); + friend JSValue jsNumber(JSGlobalData* globalData, long long i); + friend JSValue jsNumber(JSGlobalData* globalData, unsigned long long i); + + // If all bits in the mask are set, this indicates an integer number, + // if any but not all are set this value is a double precision number. + static const intptr_t TagTypeNumber = 0xffff000000000000ll; + // This value is 2^48, used to encode doubles such that the encoded value will begin + // with a 16-bit pattern within the range 0x0001..0xFFFE. + static const intptr_t DoubleEncodeOffset = 0x1000000000000ll; + static const intptr_t TagBitTypeOther = 0x2; // second bit set indicates immediate other than an integer + static const intptr_t TagMask = TagTypeNumber | TagBitTypeOther; + + static const intptr_t ExtendedTagMask = 0xC; // extended tag holds a further two bits + static const intptr_t ExtendedTagBitBool = 0x4; + static const intptr_t ExtendedTagBitUndefined = 0x8; + + static const intptr_t FullTagTypeMask = TagMask | ExtendedTagMask; + static const intptr_t FullTagTypeBool = TagBitTypeOther | ExtendedTagBitBool; + static const intptr_t FullTagTypeUndefined = TagBitTypeOther | ExtendedTagBitUndefined; + static const intptr_t FullTagTypeNull = TagBitTypeOther; + + static const int32_t IntegerPayloadShift = 0; + static const int32_t ExtendedPayloadShift = 4; + + static const intptr_t ExtendedPayloadBitBoolValue = 1 << ExtendedPayloadShift; + + static const int32_t signBit = 0x80000000; + + static ALWAYS_INLINE bool isImmediate(JSValue v) + { + return rawValue(v) & TagMask; + } + + static ALWAYS_INLINE bool isNumber(JSValue v) + { + return rawValue(v) & TagTypeNumber; + } + + static ALWAYS_INLINE bool isIntegerNumber(JSValue v) + { + return (rawValue(v) & TagTypeNumber) == TagTypeNumber; + } + + static ALWAYS_INLINE bool isDouble(JSValue v) + { + return isNumber(v) && !isIntegerNumber(v); + } + + static ALWAYS_INLINE bool isPositiveIntegerNumber(JSValue v) + { + // A single mask to check for the sign bit and the number tag all at once. + return (rawValue(v) & (signBit | TagTypeNumber)) == TagTypeNumber; + } + + static ALWAYS_INLINE bool isBoolean(JSValue v) + { + return (rawValue(v) & FullTagTypeMask) == FullTagTypeBool; + } + + static ALWAYS_INLINE bool isUndefinedOrNull(JSValue v) + { + // Undefined and null share the same value, bar the 'undefined' bit in the extended tag. + return (rawValue(v) & ~ExtendedTagBitUndefined) == FullTagTypeNull; + } + + static JSValue from(char); + static JSValue from(signed char); + static JSValue from(unsigned char); + static JSValue from(short); + static JSValue from(unsigned short); + static JSValue from(int); + static JSValue from(unsigned); + static JSValue from(long); + static JSValue from(unsigned long); + static JSValue from(long long); + static JSValue from(unsigned long long); + static JSValue from(double); + + static ALWAYS_INLINE bool isEitherImmediate(JSValue v1, JSValue v2) + { + return (rawValue(v1) | rawValue(v2)) & TagMask; + } + + static ALWAYS_INLINE bool areBothImmediate(JSValue v1, JSValue v2) + { + return isImmediate(v1) & isImmediate(v2); + } + + static ALWAYS_INLINE bool areBothImmediateIntegerNumbers(JSValue v1, JSValue v2) + { + return (rawValue(v1) & rawValue(v2) & TagTypeNumber) == TagTypeNumber; + } + + static double toDouble(JSValue); + static bool toBoolean(JSValue); + + static bool getUInt32(JSValue, uint32_t&); + static bool getTruncatedInt32(JSValue, int32_t&); + static bool getTruncatedUInt32(JSValue, uint32_t&); + + static int32_t getTruncatedInt32(JSValue); + static uint32_t getTruncatedUInt32(JSValue); + + static JSValue trueImmediate(); + static JSValue falseImmediate(); + static JSValue undefinedImmediate(); + static JSValue nullImmediate(); + static JSValue zeroImmediate(); + static JSValue oneImmediate(); + + private: + static const int minImmediateInt = ((-INT_MAX) - 1); + static const int maxImmediateInt = INT_MAX; + static const unsigned maxImmediateUInt = maxImmediateInt; + + static ALWAYS_INLINE JSValue makeValue(intptr_t integer) + { + return JSValue::makeImmediate(integer); + } + + // With USE(JSVALUE64) we want the argument to be zero extended, so the + // integer doesn't interfere with the tag bits in the upper word. In the default encoding, + // if intptr_t id larger then int32_t we sign extend the value through the upper word. + static ALWAYS_INLINE JSValue makeInt(uint32_t value) + { + return makeValue((static_cast<intptr_t>(value) << IntegerPayloadShift) | TagTypeNumber); + } + + static ALWAYS_INLINE JSValue makeDouble(double value) + { + return makeValue(reinterpretDoubleToIntptr(value) + DoubleEncodeOffset); + } + + static ALWAYS_INLINE JSValue makeBool(bool b) + { + return makeValue((static_cast<intptr_t>(b) << ExtendedPayloadShift) | FullTagTypeBool); + } + + static ALWAYS_INLINE JSValue makeUndefined() + { + return makeValue(FullTagTypeUndefined); + } + + static ALWAYS_INLINE JSValue makeNull() + { + return makeValue(FullTagTypeNull); + } + + template<typename T> + static JSValue fromNumberOutsideIntegerRange(T); + + static ALWAYS_INLINE double doubleValue(JSValue v) + { + return reinterpretIntptrToDouble(rawValue(v) - DoubleEncodeOffset); + } + + static ALWAYS_INLINE int32_t intValue(JSValue v) + { + return static_cast<int32_t>(rawValue(v) >> IntegerPayloadShift); + } + + static ALWAYS_INLINE uint32_t uintValue(JSValue v) + { + return static_cast<uint32_t>(rawValue(v) >> IntegerPayloadShift); + } + + static ALWAYS_INLINE bool boolValue(JSValue v) + { + return rawValue(v) & ExtendedPayloadBitBoolValue; + } + + static ALWAYS_INLINE intptr_t rawValue(JSValue v) + { + return v.immediateValue(); + } + }; + + ALWAYS_INLINE JSValue JSImmediate::trueImmediate() { return makeBool(true); } + ALWAYS_INLINE JSValue JSImmediate::falseImmediate() { return makeBool(false); } + ALWAYS_INLINE JSValue JSImmediate::undefinedImmediate() { return makeUndefined(); } + ALWAYS_INLINE JSValue JSImmediate::nullImmediate() { return makeNull(); } + ALWAYS_INLINE JSValue JSImmediate::zeroImmediate() { return makeInt(0); } + ALWAYS_INLINE JSValue JSImmediate::oneImmediate() { return makeInt(1); } + + inline bool doubleToBoolean(double value) + { + return value < 0.0 || value > 0.0; + } + + ALWAYS_INLINE bool JSImmediate::toBoolean(JSValue v) + { + ASSERT(isImmediate(v)); + return isNumber(v) ? isIntegerNumber(v) ? v != zeroImmediate() + : doubleToBoolean(doubleValue(v)) : v == trueImmediate(); + } + + ALWAYS_INLINE uint32_t JSImmediate::getTruncatedUInt32(JSValue v) + { + // FIXME: should probably be asserting isPositiveIntegerNumber here. + ASSERT(isIntegerNumber(v)); + return intValue(v); + } + + template<typename T> + inline JSValue JSImmediate::fromNumberOutsideIntegerRange(T value) + { + return makeDouble(static_cast<double>(value)); + } + + ALWAYS_INLINE JSValue JSImmediate::from(char i) + { + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(signed char i) + { + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(unsigned char i) + { + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(short i) + { + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(unsigned short i) + { + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(int i) + { + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(unsigned i) + { + if (i > maxImmediateUInt) + return fromNumberOutsideIntegerRange(i); + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(long i) + { + if ((i < minImmediateInt) | (i > maxImmediateInt)) + return fromNumberOutsideIntegerRange(i); + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(unsigned long i) + { + if (i > maxImmediateUInt) + return fromNumberOutsideIntegerRange(i); + return makeInt(i); + } + + ALWAYS_INLINE JSValue JSImmediate::from(long long i) + { + if ((i < minImmediateInt) | (i > maxImmediateInt)) + return JSValue(); + return makeInt(static_cast<intptr_t>(i)); + } + + ALWAYS_INLINE JSValue JSImmediate::from(unsigned long long i) + { + if (i > maxImmediateUInt) + return fromNumberOutsideIntegerRange(i); + return makeInt(static_cast<intptr_t>(i)); + } + + ALWAYS_INLINE JSValue JSImmediate::from(double d) + { + const int intVal = static_cast<int>(d); + + // Check for data loss from conversion to int. + if (intVal != d || (!intVal && signbit(d))) + return fromNumberOutsideIntegerRange(d); + + return from(intVal); + } + + ALWAYS_INLINE int32_t JSImmediate::getTruncatedInt32(JSValue v) + { + ASSERT(isIntegerNumber(v)); + return intValue(v); + } + + ALWAYS_INLINE double JSImmediate::toDouble(JSValue v) + { + ASSERT(isImmediate(v)); + + if (isIntegerNumber(v)) + return intValue(v); + + if (isNumber(v)) { + ASSERT(isDouble(v)); + return doubleValue(v); + } + + if (rawValue(v) == FullTagTypeUndefined) + return nonInlineNaN(); + + ASSERT(JSImmediate::isBoolean(v) || (v == JSImmediate::nullImmediate())); + return rawValue(v) >> ExtendedPayloadShift; + } + + ALWAYS_INLINE bool JSImmediate::getUInt32(JSValue v, uint32_t& i) + { + i = uintValue(v); + return isPositiveIntegerNumber(v); + } + + ALWAYS_INLINE bool JSImmediate::getTruncatedInt32(JSValue v, int32_t& i) + { + i = intValue(v); + return isIntegerNumber(v); + } + + ALWAYS_INLINE bool JSImmediate::getTruncatedUInt32(JSValue v, uint32_t& i) + { + return getUInt32(v, i); + } + + inline JSValue::JSValue(JSNullTag) + { + *this = JSImmediate::nullImmediate(); + } + + inline JSValue::JSValue(JSUndefinedTag) + { + *this = JSImmediate::undefinedImmediate(); + } + + inline JSValue::JSValue(JSTrueTag) + { + *this = JSImmediate::trueImmediate(); + } + + inline JSValue::JSValue(JSFalseTag) + { + *this = JSImmediate::falseImmediate(); + } + + inline bool JSValue::isUndefinedOrNull() const + { + return JSImmediate::isUndefinedOrNull(asValue()); + } + + inline bool JSValue::isBoolean() const + { + return JSImmediate::isBoolean(asValue()); + } + + inline bool JSValue::isTrue() const + { + return asValue() == JSImmediate::trueImmediate(); + } + + inline bool JSValue::isFalse() const + { + return asValue() == JSImmediate::falseImmediate(); + } + + inline bool JSValue::getBoolean(bool& v) const + { + if (JSImmediate::isBoolean(asValue())) { + v = JSImmediate::toBoolean(asValue()); + return true; + } + + return false; + } + + inline bool JSValue::getBoolean() const + { + return asValue() == jsBoolean(true); + } + + inline bool JSValue::isCell() const + { + return !JSImmediate::isImmediate(asValue()); + } + + inline bool JSValue::isInt32() const + { + return JSImmediate::isIntegerNumber(asValue()); + } + + inline int32_t JSValue::asInt32() const + { + ASSERT(isInt32()); + return JSImmediate::getTruncatedInt32(asValue()); + } + + inline bool JSValue::isUInt32() const + { + return JSImmediate::isPositiveIntegerNumber(asValue()); + } + + inline uint32_t JSValue::asUInt32() const + { + ASSERT(isUInt32()); + return JSImmediate::getTruncatedUInt32(asValue()); + } + +} // namespace JSC + +#endif // USE(JSVALUE64) + +#endif // JSImmediate_h diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp new file mode 100644 index 0000000..10f4f3f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005, 2008 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 + * 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 NU + * 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 "JSLock.h" + +#include "Collector.h" +#include "CallFrame.h" + +#if ENABLE(JSC_MULTIPLE_THREADS) +#include <pthread.h> +#endif + +namespace JSC { + +#if ENABLE(JSC_MULTIPLE_THREADS) + +// Acquire this mutex before accessing lock-related data. +static pthread_mutex_t JSMutex = PTHREAD_MUTEX_INITIALIZER; + +// Thread-specific key that tells whether a thread holds the JSMutex, and how many times it was taken recursively. +pthread_key_t JSLockCount; + +static void createJSLockCount() +{ + pthread_key_create(&JSLockCount, 0); +} + +pthread_once_t createJSLockCountOnce = PTHREAD_ONCE_INIT; + +// Lock nesting count. +intptr_t JSLock::lockCount() +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + + return reinterpret_cast<intptr_t>(pthread_getspecific(JSLockCount)); +} + +static void setLockCount(intptr_t count) +{ + ASSERT(count >= 0); + pthread_setspecific(JSLockCount, reinterpret_cast<void*>(count)); +} + +JSLock::JSLock(ExecState* exec) + : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +{ + lock(m_lockBehavior); +} + +JSLock::JSLock(JSGlobalData* globalData) + : m_lockBehavior(globalData->isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +{ + lock(m_lockBehavior); +} + +void JSLock::lock(JSLockBehavior lockBehavior) +{ +#ifdef NDEBUG + // Locking "not for real" is a debug-only feature. + if (lockBehavior == SilenceAssertionsOnly) + return; +#endif + + pthread_once(&createJSLockCountOnce, createJSLockCount); + + intptr_t currentLockCount = lockCount(); + if (!currentLockCount && lockBehavior == LockForReal) { + int result; + result = pthread_mutex_lock(&JSMutex); + ASSERT(!result); + } + setLockCount(currentLockCount + 1); +} + +void JSLock::unlock(JSLockBehavior lockBehavior) +{ + ASSERT(lockCount()); + +#ifdef NDEBUG + // Locking "not for real" is a debug-only feature. + if (lockBehavior == SilenceAssertionsOnly) + return; +#endif + + intptr_t newLockCount = lockCount() - 1; + setLockCount(newLockCount); + if (!newLockCount && lockBehavior == LockForReal) { + int result; + result = pthread_mutex_unlock(&JSMutex); + ASSERT(!result); + } +} + +void JSLock::lock(ExecState* exec) +{ + lock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); +} + +void JSLock::unlock(ExecState* exec) +{ + unlock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); +} + +bool JSLock::currentThreadIsHoldingLock() +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + return !!pthread_getspecific(JSLockCount); +} + +// This is fairly nasty. We allow multiple threads to run on the same +// context, and we do not require any locking semantics in doing so - +// clients of the API may simply use the context from multiple threads +// concurently, and assume this will work. In order to make this work, +// We lock the context when a thread enters, and unlock it when it leaves. +// However we do not only unlock when the thread returns from its +// entry point (evaluate script or call function), we also unlock the +// context if the thread leaves JSC by making a call out to an external +// function through a callback. +// +// All threads using the context share the same JS stack (the RegisterFile). +// Whenever a thread calls into JSC it starts using the RegisterFile from the +// previous 'high water mark' - the maximum point the stack has ever grown to +// (returned by RegisterFile::end()). So if a first thread calls out to a +// callback, and a second thread enters JSC, then also exits by calling out +// to a callback, we can be left with stackframes from both threads in the +// RegisterFile. As such, a problem may occur should the first thread's +// callback complete first, and attempt to return to JSC. Were we to allow +// this to happen, and were its stack to grow further, then it may potentially +// write over the second thread's call frames. +// +// In avoid JS stack corruption we enforce a policy of only ever allowing two +// threads to use a JS context concurrently, and only allowing the second of +// these threads to execute until it has completed and fully returned from its +// outermost call into JSC. We enforce this policy using 'lockDropDepth'. The +// first time a thread exits it will call DropAllLocks - which will do as expected +// and drop locks allowing another thread to enter. Should another thread, or the +// same thread again, enter JSC (through evaluate script or call function), and exit +// again through a callback, then the locks will not be dropped when DropAllLocks +// is called (since lockDropDepth is non-zero). Since this thread is still holding +// the locks, only it will re able to re-enter JSC (either be returning from the +// callback, or by re-entering through another call to evaulate script or call +// function). +// +// This policy is slightly more restricive than it needs to be for correctness - +// we could validly allow futher entries into JSC from other threads, we only +// need ensure that callbacks return in the reverse chronological order of the +// order in which they were made - though implementing the less restrictive policy +// would likely increase complexity and overhead. +// +static unsigned lockDropDepth = 0; + +JSLock::DropAllLocks::DropAllLocks(ExecState* exec) + : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + + if (lockDropDepth++) { + m_lockCount = 0; + return; + } + + m_lockCount = JSLock::lockCount(); + for (intptr_t i = 0; i < m_lockCount; i++) + JSLock::unlock(m_lockBehavior); +} + +JSLock::DropAllLocks::DropAllLocks(JSLockBehavior JSLockBehavior) + : m_lockBehavior(JSLockBehavior) +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + + if (lockDropDepth++) { + m_lockCount = 0; + return; + } + + // It is necessary to drop even "unreal" locks, because having a non-zero lock count + // will prevent a real lock from being taken. + + m_lockCount = JSLock::lockCount(); + for (intptr_t i = 0; i < m_lockCount; i++) + JSLock::unlock(m_lockBehavior); +} + +JSLock::DropAllLocks::~DropAllLocks() +{ + for (intptr_t i = 0; i < m_lockCount; i++) + JSLock::lock(m_lockBehavior); + + --lockDropDepth; +} + +#else + +JSLock::JSLock(ExecState*) + : m_lockBehavior(SilenceAssertionsOnly) +{ +} + +// If threading support is off, set the lock count to a constant value of 1 so ssertions +// that the lock is held don't fail +intptr_t JSLock::lockCount() +{ + return 1; +} + +bool JSLock::currentThreadIsHoldingLock() +{ + return true; +} + +void JSLock::lock(JSLockBehavior) +{ +} + +void JSLock::unlock(JSLockBehavior) +{ +} + +void JSLock::lock(ExecState*) +{ +} + +void JSLock::unlock(ExecState*) +{ +} + +JSLock::DropAllLocks::DropAllLocks(ExecState*) +{ +} + +JSLock::DropAllLocks::DropAllLocks(JSLockBehavior) +{ +} + +JSLock::DropAllLocks::~DropAllLocks() +{ +} + +#endif // USE(MULTIPLE_THREADS) + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSLock.h b/Source/JavaScriptCore/runtime/JSLock.h new file mode 100644 index 0000000..05b388c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005, 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 + * 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. + * + */ + +#ifndef JSLock_h +#define JSLock_h + +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> + +namespace JSC { + + // To make it safe to use JavaScript on multiple threads, it is + // important to lock before doing anything that allocates a + // JavaScript data structure or that interacts with shared state + // such as the protect count hash table. The simplest way to lock + // is to create a local JSLock object in the scope where the lock + // must be held. The lock is recursive so nesting is ok. The JSLock + // object also acts as a convenience short-hand for running important + // initialization routines. + + // To avoid deadlock, sometimes it is necessary to temporarily + // release the lock. Since it is recursive you actually have to + // release all locks held by your thread. This is safe to do if + // you are executing code that doesn't require the lock, and you + // reacquire the right number of locks at the end. You can do this + // by constructing a locally scoped JSLock::DropAllLocks object. The + // DropAllLocks object takes care to release the JSLock only if your + // thread acquired it to begin with. + + // For contexts other than the single shared one, implicit locking is not done, + // but we still need to perform all the counting in order to keep debug + // assertions working, so that clients that use the shared context don't break. + + class ExecState; + class JSGlobalData; + + enum JSLockBehavior { SilenceAssertionsOnly, LockForReal }; + + class JSLock : public Noncopyable { + public: + JSLock(ExecState*); + JSLock(JSGlobalData*); + + JSLock(JSLockBehavior lockBehavior) + : m_lockBehavior(lockBehavior) + { +#ifdef NDEBUG + // Locking "not for real" is a debug-only feature. + if (lockBehavior == SilenceAssertionsOnly) + return; +#endif + lock(lockBehavior); + } + + ~JSLock() + { +#ifdef NDEBUG + // Locking "not for real" is a debug-only feature. + if (m_lockBehavior == SilenceAssertionsOnly) + return; +#endif + unlock(m_lockBehavior); + } + + static void lock(JSLockBehavior); + static void unlock(JSLockBehavior); + static void lock(ExecState*); + static void unlock(ExecState*); + + static intptr_t lockCount(); + static bool currentThreadIsHoldingLock(); + + JSLockBehavior m_lockBehavior; + + class DropAllLocks : public Noncopyable { + public: + DropAllLocks(ExecState* exec); + DropAllLocks(JSLockBehavior); + ~DropAllLocks(); + + private: + intptr_t m_lockCount; + JSLockBehavior m_lockBehavior; + }; + }; + +} // namespace + +#endif // JSLock_h diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.cpp b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp new file mode 100644 index 0000000..e01b401 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -0,0 +1,122 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JSNotAnObject.h" + +#include <wtf/UnusedParam.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSNotAnObject); + +// JSValue methods +JSValue JSNotAnObject::toPrimitive(ExecState* exec, PreferredPrimitiveType) const +{ + ASSERT_UNUSED(exec, exec->hadException()); + return jsNumber(0); +} + +bool JSNotAnObject::getPrimitiveNumber(ExecState* exec, double&, JSValue&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::toBoolean(ExecState* exec) const +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +double JSNotAnObject::toNumber(ExecState* exec) const +{ + ASSERT_UNUSED(exec, exec->hadException()); + return NaN; +} + +UString JSNotAnObject::toString(ExecState* exec) const +{ + ASSERT_UNUSED(exec, exec->hadException()); + return ""; +} + +JSObject* JSNotAnObject::toObject(ExecState* exec) const +{ + ASSERT_UNUSED(exec, exec->hadException()); + return const_cast<JSNotAnObject*>(this); +} + +// JSObject methods +bool JSNotAnObject::getOwnPropertySlot(ExecState* exec, const Identifier&, PropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::getOwnPropertySlot(ExecState* exec, unsigned, PropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier&, PropertyDescriptor&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +void JSNotAnObject::put(ExecState* exec, const Identifier& , JSValue, PutPropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +void JSNotAnObject::put(ExecState* exec, unsigned, JSValue) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +bool JSNotAnObject::deleteProperty(ExecState* exec, const Identifier&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::deleteProperty(ExecState* exec, unsigned) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +void JSNotAnObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray&, EnumerationMode) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.h b/Source/JavaScriptCore/runtime/JSNotAnObject.h new file mode 100644 index 0000000..9f527cf --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.h @@ -0,0 +1,79 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 JSNotAnObject_h +#define JSNotAnObject_h + +#include "JSObject.h" + +namespace JSC { + + // This unholy class is used to allow us to avoid multiple exception checks + // in certain SquirrelFish bytecodes -- effectively it just silently consumes + // any operations performed on the result of a failed toObject call. + class JSNotAnObject : public JSObject { + public: + JSNotAnObject(ExecState* exec) + : JSObject(exec->globalData().notAnObjectStructure) + { + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + private: + + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + + // JSValue methods + 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; + + // 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); + + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual bool deleteProperty(ExecState*, unsigned propertyName); + + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + }; + +} // namespace JSC + +#endif // JSNotAnObject_h diff --git a/Source/JavaScriptCore/runtime/JSNumberCell.cpp b/Source/JavaScriptCore/runtime/JSNumberCell.cpp new file mode 100644 index 0000000..6fa6b2a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNumberCell.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 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 + * 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 "JSNumberCell.h" + +// Keep our exported symbols lists happy. +namespace JSC { + +JSValue jsNumberCell(ExecState*, double); + +JSValue jsNumberCell(ExecState*, double) +{ + ASSERT_NOT_REACHED(); + return JSValue(); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSNumberCell.h b/Source/JavaScriptCore/runtime/JSNumberCell.h new file mode 100644 index 0000000..0040067 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNumberCell.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2007, 2008 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 + * 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. + * + */ + +#ifndef JSNumberCell_h +#define JSNumberCell_h + +#include "CallFrame.h" +#include "JSCell.h" +#include "JSImmediate.h" +#include "Collector.h" +#include "UString.h" +#include <stddef.h> // for size_t + +namespace JSC { + + extern const double NaN; + extern const double Inf; + +#if USE(JSVALUE64) + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) + { + *this = JSImmediate::fromNumberOutsideIntegerRange(d); + } + + inline JSValue::JSValue(double d) + { + JSValue v = JSImmediate::from(d); + ASSERT(v); + *this = v; + } + + inline JSValue::JSValue(int i) + { + JSValue v = JSImmediate::from(i); + ASSERT(v); + *this = v; + } + + inline JSValue::JSValue(unsigned i) + { + JSValue v = JSImmediate::from(i); + ASSERT(v); + *this = v; + } + + inline JSValue::JSValue(long i) + { + JSValue v = JSImmediate::from(i); + ASSERT(v); + *this = v; + } + + inline JSValue::JSValue(unsigned long i) + { + JSValue v = JSImmediate::from(i); + ASSERT(v); + *this = v; + } + + inline JSValue::JSValue(long long i) + { + JSValue v = JSImmediate::from(static_cast<double>(i)); + ASSERT(v); + *this = v; + } + + inline JSValue::JSValue(unsigned long long i) + { + JSValue v = JSImmediate::from(static_cast<double>(i)); + ASSERT(v); + *this = v; + } + + inline bool JSValue::isDouble() const + { + return JSImmediate::isDouble(asValue()); + } + + inline double JSValue::asDouble() const + { + return JSImmediate::doubleValue(asValue()); + } + + inline bool JSValue::isNumber() const + { + return JSImmediate::isNumber(asValue()); + } + + inline double JSValue::uncheckedGetNumber() const + { + ASSERT(isNumber()); + return JSImmediate::toDouble(asValue()); + } + +#endif // USE(JSVALUE64) + +#if USE(JSVALUE64) + + inline JSValue::JSValue(char i) + { + ASSERT(JSImmediate::from(i)); + *this = JSImmediate::from(i); + } + + inline JSValue::JSValue(unsigned char i) + { + ASSERT(JSImmediate::from(i)); + *this = JSImmediate::from(i); + } + + inline JSValue::JSValue(short i) + { + ASSERT(JSImmediate::from(i)); + *this = JSImmediate::from(i); + } + + inline JSValue::JSValue(unsigned short i) + { + ASSERT(JSImmediate::from(i)); + *this = JSImmediate::from(i); + } + + inline JSValue jsNaN() + { + return jsNumber(NaN); + } + + // --- JSValue inlines ---------------------------- + + ALWAYS_INLINE JSValue JSValue::toJSNumber(ExecState* exec) const + { + return isNumber() ? asValue() : jsNumber(this->toNumber(exec)); + } + + inline bool JSValue::getNumber(double &result) const + { + if (isInt32()) + result = asInt32(); + else if (LIKELY(isDouble())) + result = asDouble(); + else { + ASSERT(!isNumber()); + return false; + } + return true; + } + +#endif // USE(JSVALUE64) + +} // namespace JSC + +#endif // JSNumberCell_h diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp new file mode 100644 index 0000000..9e63027 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSONObject.cpp @@ -0,0 +1,877 @@ +/* + * 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 "JSONObject.h" + +#include "BooleanObject.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "LiteralParser.h" +#include "Lookup.h" +#include "PropertyNameArray.h" +#include "UStringBuilder.h" +#include "UStringConcatenate.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSONObject); + +static EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*); +static EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*); + +} + +#include "JSONObject.lut.h" + +namespace JSC { + +JSONObject::JSONObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSObjectWithGlobalObject(globalObject, structure) +{ +} + +// PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked. +class PropertyNameForFunctionCall { +public: + PropertyNameForFunctionCall(const Identifier&); + PropertyNameForFunctionCall(unsigned); + + JSValue value(ExecState*) const; + +private: + const Identifier* m_identifier; + unsigned m_number; + mutable JSValue m_value; +}; + +class Stringifier : public Noncopyable { +public: + Stringifier(ExecState*, JSValue replacer, JSValue space); + ~Stringifier(); + JSValue stringify(JSValue); + + void markAggregate(MarkStack&); + +private: + class Holder { + public: + Holder(JSObject*); + + JSObject* object() const { return m_object; } + + bool appendNextProperty(Stringifier&, UStringBuilder&); + + private: + JSObject* const m_object; + const bool m_isArray; + bool m_isJSArray; + unsigned m_index; + unsigned m_size; + RefPtr<PropertyNameArrayData> m_propertyNames; + }; + + friend class Holder; + + static void appendQuotedString(UStringBuilder&, const UString&); + + JSValue toJSON(JSValue, const PropertyNameForFunctionCall&); + + enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue }; + StringifyResult appendStringifiedValue(UStringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&); + + bool willIndent() const; + void indent(); + void unindent(); + void startNewLine(UStringBuilder&) const; + + Stringifier* const m_nextStringifierToMark; + ExecState* const m_exec; + const JSValue m_replacer; + bool m_usingArrayReplacer; + PropertyNameArray m_arrayReplacerPropertyNames; + CallType m_replacerCallType; + CallData m_replacerCallData; + const UString m_gap; + + HashSet<JSObject*> m_holderCycleDetector; + Vector<Holder, 16> m_holderStack; + UString m_repeatedGap; + UString m_indent; +}; + +// ------------------------------ helper functions -------------------------------- + +static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value) +{ + if (!value.isObject()) + return value; + JSObject* object = asObject(value); + if (object->inherits(&NumberObject::info)) + return jsNumber(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(ExecState* exec, JSValue space) +{ + const unsigned 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)) { + int count; + if (spaceCount > maxGapLength) + count = maxGapLength; + else if (!(spaceCount > 0)) + count = 0; + else + count = static_cast<int>(spaceCount); + 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. + UString spaces = space.getString(exec); + if (spaces.length() > maxGapLength) { + spaces = spaces.substringSharingImpl(0, maxGapLength); + } + return spaces; +} + +// ------------------------------ PropertyNameForFunctionCall -------------------------------- + +inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier) + : m_identifier(&identifier) +{ +} + +inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number) + : m_identifier(0) + , m_number(number) +{ +} + +JSValue PropertyNameForFunctionCall::value(ExecState* exec) const +{ + if (!m_value) { + if (m_identifier) + m_value = jsString(exec, m_identifier->ustring()); + else + m_value = jsNumber(m_number); + } + return m_value; +} + +// ------------------------------ Stringifier -------------------------------- + +Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space) + : m_nextStringifierToMark(exec->globalData().firstStringifierToMark) + , m_exec(exec) + , m_replacer(replacer) + , m_usingArrayReplacer(false) + , m_arrayReplacerPropertyNames(exec) + , m_replacerCallType(CallTypeNone) + , m_gap(gap(exec, space)) +{ + exec->globalData().firstStringifierToMark = this; + + if (!m_replacer.isObject()) + return; + + if (asObject(m_replacer)->inherits(&JSArray::info)) { + m_usingArrayReplacer = true; + JSObject* array = asObject(m_replacer); + unsigned length = array->get(exec, exec->globalData().propertyNames->length).toUInt32(exec); + for (unsigned i = 0; i < length; ++i) { + JSValue name = array->get(exec, i); + if (exec->hadException()) + break; + + UString propertyName; + if (name.getString(exec, propertyName)) { + m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); + continue; + } + + 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; + } + + m_replacerCallType = asObject(m_replacer)->getCallData(m_replacerCallData); +} + +Stringifier::~Stringifier() +{ + ASSERT(m_exec->globalData().firstStringifierToMark == this); + m_exec->globalData().firstStringifierToMark = m_nextStringifierToMark; +} + +void Stringifier::markAggregate(MarkStack& markStack) +{ + for (Stringifier* stringifier = this; stringifier; stringifier = stringifier->m_nextStringifierToMark) { + size_t size = m_holderStack.size(); + for (size_t i = 0; i < size; ++i) + markStack.append(m_holderStack[i].object()); + } +} + +JSValue Stringifier::stringify(JSValue value) +{ + JSObject* object = constructEmptyObject(m_exec); + if (m_exec->hadException()) + return jsNull(); + + PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier); + object->putDirect(m_exec->globalData().propertyNames->emptyIdentifier, value); + + UStringBuilder result; + if (appendStringifiedValue(result, value, object, emptyPropertyName) != StringifySucceeded) + return jsUndefined(); + if (m_exec->hadException()) + return jsNull(); + + return jsString(m_exec, result.toUString()); +} + +void Stringifier::appendQuotedString(UStringBuilder& builder, const UString& value) +{ + int length = value.length(); + + // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters. + builder.reserveCapacity(builder.length() + length + 2 + 8); + + builder.append('"'); + + const UChar* data = value.characters(); + for (int i = 0; i < length; ++i) { + int start = i; + while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\')) + ++i; + builder.append(data + start, i - start); + if (i >= length) + break; + switch (data[i]) { + case '\t': + builder.append('\\'); + builder.append('t'); + break; + case '\r': + builder.append('\\'); + builder.append('r'); + break; + case '\n': + builder.append('\\'); + builder.append('n'); + break; + case '\f': + builder.append('\\'); + builder.append('f'); + break; + case '\b': + builder.append('\\'); + builder.append('b'); + break; + case '"': + builder.append('\\'); + builder.append('"'); + break; + case '\\': + builder.append('\\'); + builder.append('\\'); + break; + default: + static const char hexDigits[] = "0123456789abcdef"; + UChar ch = data[i]; + UChar hex[] = { '\\', 'u', hexDigits[(ch >> 12) & 0xF], hexDigits[(ch >> 8) & 0xF], hexDigits[(ch >> 4) & 0xF], hexDigits[ch & 0xF] }; + builder.append(hex, WTF_ARRAY_LENGTH(hex)); + break; + } + } + + builder.append('"'); +} + +inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName) +{ + ASSERT(!m_exec->hadException()); + if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->globalData().propertyNames->toJSON)) + return value; + + JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->globalData().propertyNames->toJSON); + if (m_exec->hadException()) + return jsNull(); + + if (!toJSONFunction.isObject()) + return value; + + JSObject* object = asObject(toJSONFunction); + CallData callData; + CallType callType = object->getCallData(callData); + if (callType == CallTypeNone) + return value; + + JSValue list[] = { propertyName.value(m_exec) }; + ArgList args(list, WTF_ARRAY_LENGTH(list)); + return call(m_exec, object, callType, callData, value, args); +} + +Stringifier::StringifyResult Stringifier::appendStringifiedValue(UStringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName) +{ + // Call the toJSON function. + value = toJSON(value, propertyName); + if (m_exec->hadException()) + return StringifyFailed; + + // Call the replacer function. + if (m_replacerCallType != CallTypeNone) { + JSValue list[] = { propertyName.value(m_exec), value }; + ArgList args(list, WTF_ARRAY_LENGTH(list)); + value = call(m_exec, m_replacer, m_replacerCallType, m_replacerCallData, holder, args); + if (m_exec->hadException()) + return StringifyFailed; + } + + if (value.isUndefined() && !holder->inherits(&JSArray::info)) + return StringifyFailedDueToUndefinedValue; + + if (value.isNull()) { + builder.append("null"); + return StringifySucceeded; + } + + value = unwrapBoxedPrimitive(m_exec, value); + + if (m_exec->hadException()) + return StringifyFailed; + + if (value.isBoolean()) { + builder.append(value.getBoolean() ? "true" : "false"); + return StringifySucceeded; + } + + UString stringValue; + if (value.getString(m_exec, stringValue)) { + appendQuotedString(builder, stringValue); + return StringifySucceeded; + } + + double numericValue; + if (value.getNumber(numericValue)) { + if (!isfinite(numericValue)) + builder.append("null"); + else + builder.append(UString::number(numericValue)); + return StringifySucceeded; + } + + if (!value.isObject()) + return StringifyFailed; + + 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, createTypeError(m_exec, "JSON.stringify cannot serialize cyclic structures.")); + return StringifyFailed; + } + bool holderStackWasEmpty = m_holderStack.isEmpty(); + m_holderStack.append(object); + if (!holderStackWasEmpty) + return StringifySucceeded; + + // If this is the outermost call, then loop to handle everything on the holder stack. + TimeoutChecker localTimeoutChecker(m_exec->globalData().timeoutChecker); + localTimeoutChecker.reset(); + unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck(); + do { + while (m_holderStack.last().appendNextProperty(*this, builder)) { + if (m_exec->hadException()) + return StringifyFailed; + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) { + throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); + return StringifyFailed; + } + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + } + m_holderCycleDetector.remove(m_holderStack.last().object()); + m_holderStack.removeLast(); + } while (!m_holderStack.isEmpty()); + return StringifySucceeded; +} + +inline bool Stringifier::willIndent() const +{ + return !m_gap.isEmpty(); +} + +inline void Stringifier::indent() +{ + // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent. + unsigned newSize = m_indent.length() + m_gap.length(); + if (newSize > m_repeatedGap.length()) + m_repeatedGap = makeUString(m_repeatedGap, m_gap); + ASSERT(newSize <= m_repeatedGap.length()); + m_indent = m_repeatedGap.substringSharingImpl(0, newSize); +} + +inline void Stringifier::unindent() +{ + ASSERT(m_indent.length() >= m_gap.length()); + m_indent = m_repeatedGap.substringSharingImpl(0, m_indent.length() - m_gap.length()); +} + +inline void Stringifier::startNewLine(UStringBuilder& builder) const +{ + if (m_gap.isEmpty()) + return; + builder.append('\n'); + builder.append(m_indent); +} + +inline Stringifier::Holder::Holder(JSObject* object) + : m_object(object) + , m_isArray(object->inherits(&JSArray::info)) + , m_index(0) +{ +} + +bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, UStringBuilder& builder) +{ + ASSERT(m_index <= m_size); + + ExecState* exec = stringifier.m_exec; + + // First time through, initialize. + if (!m_index) { + if (m_isArray) { + m_isJSArray = isJSArray(&exec->globalData(), m_object); + m_size = m_object->get(exec, exec->globalData().propertyNames->length).toUInt32(exec); + builder.append('['); + } else { + if (stringifier.m_usingArrayReplacer) + m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data(); + else { + PropertyNameArray objectPropertyNames(exec); + m_object->getOwnPropertyNames(exec, objectPropertyNames); + m_propertyNames = objectPropertyNames.releaseData(); + } + m_size = m_propertyNames->propertyNameVector().size(); + builder.append('{'); + } + stringifier.indent(); + } + + // Last time through, finish up and return false. + if (m_index == m_size) { + stringifier.unindent(); + if (m_size && builder[builder.length() - 1] != '{') + stringifier.startNewLine(builder); + builder.append(m_isArray ? ']' : '}'); + return false; + } + + // Handle a single element of the array or object. + unsigned index = m_index++; + unsigned rollBackPoint = 0; + StringifyResult stringifyResult; + if (m_isArray) { + // Get the value. + JSValue value; + if (m_isJSArray && asArray(m_object)->canGetIndex(index)) + value = asArray(m_object)->getIndex(index); + else { + PropertySlot slot(m_object); + if (!m_object->getOwnPropertySlot(exec, index, slot)) + slot.setUndefined(); + if (exec->hadException()) + return false; + value = slot.getValue(exec, index); + } + + // Append the separator string. + if (index) + builder.append(','); + stringifier.startNewLine(builder); + + // Append the stringified value. + stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, index); + } else { + // Get the value. + PropertySlot slot(m_object); + Identifier& propertyName = m_propertyNames->propertyNameVector()[index]; + if (!m_object->getOwnPropertySlot(exec, propertyName, slot)) + return true; + JSValue value = slot.getValue(exec, propertyName); + if (exec->hadException()) + return false; + + rollBackPoint = builder.length(); + + // Append the separator string. + if (builder[rollBackPoint - 1] != '{') + builder.append(','); + stringifier.startNewLine(builder); + + // Append the property name. + appendQuotedString(builder, propertyName.ustring()); + builder.append(':'); + if (stringifier.willIndent()) + builder.append(' '); + + // Append the stringified value. + stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, propertyName); + } + + // From this point on, no access to the this pointer or to any members, because the + // Holder object may have moved if the call to stringify pushed a new Holder onto + // m_holderStack. + + switch (stringifyResult) { + case StringifyFailed: + builder.append("null"); + break; + case StringifySucceeded: + break; + case StringifyFailedDueToUndefinedValue: + // This only occurs when get an undefined value for an object property. + // In this case we don't want the separator and property name that we + // already appended, so roll back. + builder.resize(rollBackPoint); + break; + } + + return true; +} + +// ------------------------------ JSONObject -------------------------------- + +const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable }; + +/* Source for JSONObject.lut.h +@begin jsonTable + parse JSONProtoFuncParse DontEnum|Function 2 + stringify JSONProtoFuncStringify DontEnum|Function 3 +@end +*/ + +// ECMA 15.8 + +bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, slot); +} + +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) +{ + stringifier->markAggregate(markStack); +} + +class Walker { +public: + Walker(ExecState* exec, JSObject* function, CallType callType, CallData callData) + : m_exec(exec) + , m_function(function) + , m_callType(callType) + , m_callData(callData) + { + } + JSValue walk(JSValue unfiltered); +private: + 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, thisObj, argList); + } + + friend class Holder; + + ExecState* m_exec; + JSObject* m_function; + 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) +{ + Vector<PropertyNameArray, 16> propertyStack; + Vector<uint32_t, 16> indexStack; + Vector<JSObject*, 16> objectStack; + Vector<JSArray*, 16> arrayStack; + + Vector<WalkerState, 16> stateStack; + 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)) || asObject(inValue)->inherits(&JSArray::info)); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwError(m_exec, createStackOverflowError(m_exec)); + + JSArray* array = asArray(inValue); + arrayStack.append(array); + indexStack.append(0); + // fallthrough + } + arrayStartVisitMember: + case ArrayStartVisitMember: { + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) + return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + + JSArray* array = arrayStack.last(); + uint32_t index = indexStack.last(); + if (index == array->length()) { + outValue = array; + arrayStack.removeLast(); + indexStack.removeLast(); + break; + } + 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; + } else + outValue = inValue; + // fallthrough + } + case ArrayEndVisitMember: { + JSArray* array = arrayStack.last(); + JSValue filteredValue = callReviver(array, jsString(m_exec, UString::number(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()++; + goto arrayStartVisitMember; + } + objectStartState: + case ObjectStartState: { + ASSERT(inValue.isObject()); + ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::info)); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwError(m_exec, createStackOverflowError(m_exec)); + + JSObject* object = asObject(inValue); + objectStack.append(object); + indexStack.append(0); + propertyStack.append(PropertyNameArray(m_exec)); + object->getOwnPropertyNames(m_exec, propertyStack.last()); + // fallthrough + } + objectStartVisitMember: + case ObjectStartVisitMember: { + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) + return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + + JSObject* object = objectStack.last(); + uint32_t index = indexStack.last(); + PropertyNameArray& properties = propertyStack.last(); + if (index == properties.size()) { + outValue = object; + objectStack.removeLast(); + indexStack.removeLast(); + propertyStack.removeLast(); + break; + } + PropertySlot slot; + 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; + } else + outValue = inValue; + // fallthrough + } + case ObjectEndVisitMember: { + JSObject* object = objectStack.last(); + Identifier prop = propertyStack.last()[indexStack.last()]; + PutPropertySlot 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()++; + goto objectStartVisitMember; + } + stateUnknown: + case StateUnknown: + if (!inValue.isObject()) { + outValue = inValue; + break; + } + 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)) + return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + } + 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 +EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec) +{ + if (!exec->argumentCount()) + return throwVMError(exec, createError(exec, "JSON.parse requires at least one parameter")); + JSValue value = exec->argument(0); + UString source = value.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + + LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON); + JSValue unfiltered = jsonParser.tryLiteralParse(); + if (!unfiltered) + return throwVMError(exec, createSyntaxError(exec, "Unable to parse JSON string")); + + if (exec->argumentCount() < 2) + return JSValue::encode(unfiltered); + + JSValue function = exec->argument(1); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return JSValue::encode(unfiltered); + return JSValue::encode(Walker(exec, asObject(function), callType, callData).walk(unfiltered)); +} + +// ECMA-262 v5 15.12.3 +EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec) +{ + if (!exec->argumentCount()) + return throwVMError(exec, createError(exec, "No input to stringify")); + JSValue value = exec->argument(0); + JSValue replacer = exec->argument(1); + JSValue space = exec->argument(2); + return JSValue::encode(Stringifier(exec, replacer, space).stringify(value)); +} + +UString JSONStringify(ExecState* exec, JSValue value, unsigned indent) +{ + JSValue result = Stringifier(exec, jsNull(), jsNumber(indent)).stringify(value); + if (result.isUndefinedOrNull()) + return UString(); + return result.getString(exec); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSONObject.h b/Source/JavaScriptCore/runtime/JSONObject.h new file mode 100644 index 0000000..f64be12 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSONObject.h @@ -0,0 +1,61 @@ +/* + * 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 JSONObject_h +#define JSONObject_h + +#include "JSObjectWithGlobalObject.h" + +namespace JSC { + + class Stringifier; + + class JSONObject : public JSObjectWithGlobalObject { + public: + JSONObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure); + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + static void markStringifiers(MarkStack&, Stringifier*); + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObject::StructureFlags; + + 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; + }; + + UString JSONStringify(ExecState* exec, JSValue value, unsigned indent); + +} // namespace JSC + +#endif // JSONObject_h diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp new file mode 100644 index 0000000..30e40e4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -0,0 +1,722 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel (eric@webkit.org) + * + * 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 "JSObject.h" + +#include "DatePrototype.h" +#include "ErrorConstructor.h" +#include "GetterSetter.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "NativeErrorConstructor.h" +#include "ObjectPrototype.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.h" +#include "Lookup.h" +#include "Nodes.h" +#include "Operations.h" +#include <math.h> +#include <wtf/Assertions.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSObject); + +const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; + +static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + // Add properties from the static hashtables of properties + for (; classInfo; classInfo = classInfo->parentClass) { + const HashTable* table = classInfo->propHashTable(exec); + if (!table) + continue; + table->initializeIfNeeded(exec); + ASSERT(table->table); + + int hashSizeMask = table->compactSize - 1; + const HashEntry* entry = table->table; + for (int i = 0; i <= hashSizeMask; ++i, ++entry) { + if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties))) + propertyNames.add(entry->key()); + } + } +} + +void JSObject::markChildren(MarkStack& markStack) +{ +#ifndef NDEBUG + bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation; + markStack.m_isCheckingForDefaultMarkViolation = false; +#endif + + markChildrenDirect(markStack); + +#ifndef NDEBUG + markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; +#endif +} + +UString JSObject::className() const +{ + const ClassInfo* info = classInfo(); + if (info) + return info->className; + return "Object"; +} + +bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); +} + +static void throwSetterError(ExecState* exec) +{ + throwError(exec, createTypeError(exec, "setting a property that has only a getter")); +} + +// ECMA 8.6.2.2 +void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + if (propertyName == exec->propertyNames().underscoreProto) { + // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. + if (!value.isObject() && !value.isNull()) + return; + if (!setPrototypeWithCycleCheck(value)) + throwError(exec, createError(exec, "cyclic __proto__ value")); + return; + } + + // Check if there are any setters or getters in the prototype chain + JSValue prototype; + for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { + prototype = obj->prototype(); + if (prototype.isNull()) { + if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + } + + unsigned attributes; + JSCell* specificValue; + if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) { + if (slot.isStrictMode()) + throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + return; + } + + for (JSObject* obj = this; ; obj = asObject(prototype)) { + if (JSValue gs = obj->getDirect(propertyName)) { + if (gs.isGetterSetter()) { + JSObject* setterFunc = asGetterSetter(gs)->setter(); + if (!setterFunc) { + throwSetterError(exec); + return; + } + + CallData callData; + CallType callType = setterFunc->getCallData(callData); + MarkedArgumentBuffer args; + args.append(value); + call(exec, setterFunc, callType, callData, this, args); + return; + } + + // If there's an existing property on the object or one of its + // prototypes it should be replaced, so break here. + break; + } + + prototype = obj->prototype(); + if (prototype.isNull()) + break; + } + + if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; +} + +void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value) +{ + PutPropertySlot slot; + put(exec, Identifier::from(exec, propertyName), value, slot); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + putDirectInternal(*globalData, propertyName, value, attributes); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes) +{ + putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes); +} + +void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot); +} + +void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + putDirectInternal(exec->globalData(), propertyName, value, attributes); +} + +void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes) +{ + putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes); +} + +bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const +{ + PropertySlot slot; + return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); +} + +bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot; + return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); +} + +// ECMA 8.6.2.5 +bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + unsigned attributes; + JSCell* specificValue; + if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) { + if ((attributes & DontDelete)) + return false; + removeDirect(propertyName); + return true; + } + + // Look in the static hashtable of properties + const HashEntry* entry = findPropertyHashEntry(exec, propertyName); + if (entry && entry->attributes() & DontDelete) + return false; // this builtin property can't be deleted + + // FIXME: Should the code here actually do some deletion? + return true; +} + +bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const +{ + PropertySlot slot; + return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot); +} + +bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName) +{ + return deleteProperty(exec, Identifier::from(exec, propertyName)); +} + +static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName) +{ + JSValue function = object->get(exec, propertyName); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return exec->exception(); + + // Prevent "toString" and "valueOf" from observing execution if an exception + // is pending. + if (exec->hadException()) + return exec->exception(); + + JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList()); + ASSERT(!result.isGetterSetter()); + if (exec->hadException()) + return exec->exception(); + if (result.isObject()) + return JSValue(); + return result; +} + +bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) +{ + result = defaultValue(exec, PreferNumber); + number = result.toNumber(exec); + return !result.isString(); +} + +// ECMA 8.6.2.6 +JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + // Must call toString first for Date objects. + if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) { + JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); + if (value) + return value; + value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); + if (value) + return value; + } else { + JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); + if (value) + return value; + value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); + if (value) + return value; + } + + ASSERT(!exec->hadException()); + + return throwError(exec, createTypeError(exec, "No default value")); +} + +const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const +{ + for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { + if (const HashTable* propHashTable = info->propHashTable(exec)) { + if (const HashEntry* entry = propHashTable->entry(exec, propertyName)) + return entry; + } + } + return 0; +} + +void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) +{ + JSValue object = getDirect(propertyName); + if (object && object.isGetterSetter()) { + ASSERT(m_structure->hasGetterSetterProperties()); + asGetterSetter(object)->setGetter(getterFunction); + return; + } + + PutPropertySlot slot; + GetterSetter* getterSetter = new (exec) GetterSetter(exec); + 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 + // if we override an existing non-getter or non-setter. + if (slot.type() != PutPropertySlot::NewProperty) { + if (!m_structure->isDictionary()) { + RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); + setStructure(structure.release()); + } + } + + m_structure->setHasGetterSetterProperties(true); + getterSetter->setGetter(getterFunction); +} + +void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) +{ + JSValue object = getDirect(propertyName); + if (object && object.isGetterSetter()) { + ASSERT(m_structure->hasGetterSetterProperties()); + asGetterSetter(object)->setSetter(setterFunction); + return; + } + + PutPropertySlot slot; + GetterSetter* getterSetter = new (exec) GetterSetter(exec); + 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 + // if we override an existing non-getter or non-setter. + if (slot.type() != PutPropertySlot::NewProperty) { + if (!m_structure->isDictionary()) { + RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); + setStructure(structure.release()); + } + } + + m_structure->setHasGetterSetterProperties(true); + getterSetter->setSetter(setterFunction); +} + +JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName) +{ + JSObject* object = this; + while (true) { + if (JSValue value = object->getDirect(propertyName)) { + if (!value.isGetterSetter()) + return jsUndefined(); + JSObject* functionObject = asGetterSetter(value)->getter(); + if (!functionObject) + return jsUndefined(); + return functionObject; + } + + if (!object->prototype() || !object->prototype().isObject()) + return jsUndefined(); + object = asObject(object->prototype()); + } +} + +JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName) +{ + JSObject* object = this; + while (true) { + if (JSValue value = object->getDirect(propertyName)) { + if (!value.isGetterSetter()) + return jsUndefined(); + JSObject* functionObject = asGetterSetter(value)->setter(); + if (!functionObject) + return jsUndefined(); + return functionObject; + } + + if (!object->prototype() || !object->prototype().isObject()) + return jsUndefined(); + object = asObject(object->prototype()); + } +} + +bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) +{ + if (!value.isObject()) + return false; + + if (!proto.isObject()) { + throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property.")); + return false; + } + + JSObject* object = asObject(value); + while ((object = object->prototype().getObject())) { + if (proto == object) + return true; + } + return false; +} + +bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const +{ + PropertyDescriptor descriptor; + if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) + return false; + return descriptor.enumerable(); +} + +bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const +{ + unsigned attributes; + if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) + return true; + + // This could be a function within the static table? - should probably + // also look in the hash? This currently should not be a problem, since + // we've currently always call 'get' first, which should have populated + // the normal storage. + return false; +} + +void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + getOwnPropertyNames(exec, propertyNames, mode); + + if (prototype().isNull()) + return; + + JSObject* prototype = asObject(this->prototype()); + while(1) { + if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { + prototype->getPropertyNames(exec, propertyNames, mode); + break; + } + prototype->getOwnPropertyNames(exec, propertyNames, mode); + JSValue nextProto = prototype->prototype(); + if (nextProto.isNull()) + break; + prototype = asObject(nextProto); + } +} + +void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + m_structure->getPropertyNames(propertyNames, mode); + getClassPropertyNames(exec, classInfo(), propertyNames, mode); +} + +bool JSObject::toBoolean(ExecState*) const +{ + return true; +} + +double JSObject::toNumber(ExecState* exec) const +{ + JSValue primitive = toPrimitive(exec, PreferNumber); + if (exec->hadException()) // should be picked up soon in Nodes.cpp + return 0.0; + return primitive.toNumber(exec); +} + +UString JSObject::toString(ExecState* exec) const +{ + JSValue primitive = toPrimitive(exec, PreferString); + if (exec->hadException()) + return ""; + return primitive.toString(exec); +} + +JSObject* JSObject::toObject(ExecState*) const +{ + return const_cast<JSObject*>(this); +} + +JSObject* JSObject::toThisObject(ExecState*) const +{ + return const_cast<JSObject*>(this); +} + +JSValue JSObject::toStrictThisObject(ExecState*) const +{ + return const_cast<JSObject*>(this); +} + +JSObject* JSObject::unwrappedObject() +{ + return this; +} + +void JSObject::removeDirect(const Identifier& propertyName) +{ + size_t offset; + if (m_structure->isUncacheableDictionary()) { + offset = m_structure->removePropertyWithoutTransition(propertyName); + if (offset != WTF::notFound) + putDirectOffset(offset, jsUndefined()); + return; + } + + RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset); + setStructure(structure.release()); + if (offset != WTF::notFound) + putDirectOffset(offset, jsUndefined()); +} + +void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) +{ + putDirectFunction(Identifier(exec, function->name(exec)), function, attr); +} + +void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr) +{ + putDirectFunction(Identifier(exec, function->name(exec)), function, attr); +} + +void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) +{ + putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); +} + +void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr) +{ + putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); +} + +NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) +{ + if (JSObject* getterFunction = asGetterSetter(*location)->getter()) { + if (!structure()->isDictionary()) + slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); + else + slot.setGetterSlot(getterFunction); + } else + slot.setUndefined(); +} + +Structure* JSObject::createInheritorID() +{ + m_inheritorID = JSObject::createStructure(this); + return m_inheritorID.get(); +} + +void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize) +{ + allocatePropertyStorageInline(oldSize, newSize); +} + +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) +{ + 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(exec, descriptor)) + return true; + + // Filter out invalid changes + if (!current.configurable()) { + if (descriptor.configurable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { + if (throwException) + throwError(exec, createTypeError(exec, "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, createTypeError(exec, "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, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); + return false; + } + if (!current.writable()) { + if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) { + if (throwException) + throwError(exec, createTypeError(exec, "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(exec, current.setter(), descriptor.setter()))) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property.")); + return false; + } + if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { + if (throwException) + throwError(exec, createTypeError(exec, "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; +} + +JSObject* throwTypeError(ExecState* exec, const UString& message) +{ + return throwError(exec, createTypeError(exec, message)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h new file mode 100644 index 0000000..803abfd --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -0,0 +1,771 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef JSObject_h +#define JSObject_h + +#include "ArgList.h" +#include "ClassInfo.h" +#include "CommonIdentifiers.h" +#include "Completion.h" +#include "CallFrame.h" +#include "JSCell.h" +#include "JSNumberCell.h" +#include "MarkStack.h" +#include "PropertySlot.h" +#include "PutPropertySlot.h" +#include "ScopeChain.h" +#include "Structure.h" +#include "JSGlobalData.h" +#include "JSString.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + + inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value) + { + if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr)) + return value.asCell(); + return 0; + } + + class HashEntry; + class InternalFunction; + class PropertyDescriptor; + class PropertyNameArray; + class Structure; + struct HashTable; + + JSObject* throwTypeError(ExecState*, const UString&); + extern const char* StrictModeReadonlyPropertyWriteError; + + // ECMA 262-3 8.6.1 + // Property attributes + enum Attribute { + None = 0, + ReadOnly = 1 << 1, // property can be only read, not written + DontEnum = 1 << 2, // property doesn't appear in (for .. in ..) + DontDelete = 1 << 3, // property can't be deleted + Function = 1 << 4, // property is a function - only used by static hashtables + Getter = 1 << 5, // property is a getter + Setter = 1 << 6 // property is a setter + }; + + typedef EncodedJSValue* PropertyStorage; + typedef const EncodedJSValue* ConstPropertyStorage; + + class JSObject : public JSCell { + friend class BatchedTransitionOptimizer; + friend class JIT; + friend class JSCell; + friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); + + public: + 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(); + + JSValue prototype() const; + void setPrototype(JSValue prototype); + bool setPrototypeWithCycleCheck(JSValue prototype); + + void setStructure(NonNullPassRefPtr<Structure>); + Structure* inheritorID(); + + virtual UString className() const; + + JSValue get(ExecState*, const Identifier& propertyName) const; + JSValue get(ExecState*, unsigned propertyName) const; + + 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); + + virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); + virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes); + virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes); + virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); + virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); + virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes); + + bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const; + + bool hasProperty(ExecState*, const Identifier& propertyName) const; + bool hasProperty(ExecState*, unsigned propertyName) const; + bool hasOwnProperty(ExecState*, const Identifier& propertyName) const; + + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual bool deleteProperty(ExecState*, unsigned propertyName); + + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) 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; + + virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; + virtual JSObject* unwrappedObject(); + + bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; + + // This get function only looks at the property map. + JSValue getDirect(const Identifier& propertyName) const + { + size_t offset = m_structure->get(propertyName); + return offset != WTF::notFound ? getDirectOffset(offset) : JSValue(); + } + + JSValue* getDirectLocation(const Identifier& propertyName) + { + size_t offset = m_structure->get(propertyName); + return offset != WTF::notFound ? locationForOffset(offset) : 0; + } + + JSValue* getDirectLocation(const Identifier& propertyName, unsigned& attributes) + { + JSCell* specificFunction; + size_t offset = m_structure->get(propertyName, attributes, specificFunction); + return offset != WTF::notFound ? locationForOffset(offset) : 0; + } + + size_t offsetForLocation(JSValue* location) const + { + return location - reinterpret_cast<const JSValue*>(propertyStorage()); + } + + void transitionTo(Structure*); + + void removeDirect(const Identifier& propertyName); + bool hasCustomProperties() { return !m_structure->isEmpty(); } + bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); } + + bool putDirect(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + void putDirect(const Identifier& propertyName, JSValue value, unsigned attr = 0); + bool putDirect(const Identifier& propertyName, JSValue value, PutPropertySlot&); + + void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr = 0); + void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0); + + void putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attr = 0); + void putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attr = 0); + void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0); + + // Fast access to known property offsets. + JSValue getDirectOffset(size_t offset) const { return JSValue::decode(propertyStorage()[offset]); } + void putDirectOffset(size_t offset, JSValue value) { propertyStorage()[offset] = JSValue::encode(value); } + + void fillGetterPropertySlot(PropertySlot&, JSValue* location); + + 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; } + virtual bool isActivationObject() const { return false; } + virtual bool isStrictModeFunction() const { return false; } + virtual bool isErrorInstance() const { return false; } + + virtual ComplType exceptionType() const { return Throw; } + + void allocatePropertyStorage(size_t oldSize, size_t newSize); + void allocatePropertyStorageInline(size_t oldSize, size_t newSize); + bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } + + static const unsigned inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3; + static const unsigned nonInlineBaseStorageCapacity = 16; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + void flattenDictionaryObject() + { + m_structure->flattenDictionaryStructure(this); + } + + void putAnonymousValue(unsigned index, JSValue value) + { + ASSERT(index < m_structure->anonymousSlotCount()); + *locationForOffset(index) = value; + } + JSValue getAnonymousValue(unsigned index) const + { + ASSERT(index < m_structure->anonymousSlotCount()); + return *locationForOffset(index); + } + + protected: + static const unsigned StructureFlags = 0; + + 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(ExecState* exec); + void isObject(); + void isString(); + + ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } + PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } + + const JSValue* locationForOffset(size_t offset) const + { + return reinterpret_cast<const JSValue*>(&propertyStorage()[offset]); + } + + JSValue* locationForOffset(size_t offset) + { + return reinterpret_cast<JSValue*>(&propertyStorage()[offset]); + } + + bool putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot, JSCell*); + bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); + + bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + + const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; + Structure* createInheritorID(); + + union { + PropertyStorage m_externalStorage; + EncodedJSValue m_inlineStorage[inlineStorageCapacity]; + }; + + RefPtr<Structure> m_inheritorID; + }; + +inline JSObject* asObject(JSCell* cell) +{ + ASSERT(cell->isObject()); + return static_cast<JSObject*>(cell); +} + +inline JSObject* asObject(JSValue value) +{ + return asObject(value.asCell()); +} + +inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure) + : JSCell(structure.releaseRef()) // ~JSObject balances this ref() +{ + ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); + ASSERT(m_structure->isEmpty()); + ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); + ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0); +} + +inline JSObject::~JSObject() +{ + ASSERT(m_structure); + if (!isUsingInlineStorage()) + delete [] m_externalStorage; + m_structure->deref(); +} + +inline JSValue JSObject::prototype() const +{ + return m_structure->storedPrototype(); +} + +inline bool JSObject::setPrototypeWithCycleCheck(JSValue prototype) +{ + JSValue nextPrototypeValue = prototype; + while (nextPrototypeValue && nextPrototypeValue.isObject()) { + JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); + if (nextPrototype == this) + return false; + nextPrototypeValue = nextPrototype->prototype(); + } + setPrototype(prototype); + return true; +} + +inline void JSObject::setPrototype(JSValue prototype) +{ + ASSERT(prototype); + RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype); + setStructure(newStructure.release()); +} + +inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure) +{ + m_structure->deref(); + m_structure = structure.leakRef(); // ~JSObject balances this ref() +} + +inline Structure* JSObject::inheritorID() +{ + if (m_inheritorID) + return m_inheritorID.get(); + return createInheritorID(); +} + +inline bool Structure::isUsingInlineStorage() const +{ + return (propertyStorageCapacity() == JSObject::inlineStorageCapacity); +} + +inline bool JSCell::inherits(const ClassInfo* info) const +{ + for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { + if (ci == info) + return true; + } + return false; +} + +// this method is here to be after the inline declaration of JSCell::inherits +inline bool JSValue::inherits(const ClassInfo* classInfo) const +{ + return isCell() && asCell()->inherits(classInfo); +} + +ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (JSValue* location = getDirectLocation(propertyName)) { + if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter()) + fillGetterPropertySlot(slot, location); + else + slot.setValueSlot(this, location, offsetForLocation(location)); + return true; + } + + // non-standard Netscape extension + if (propertyName == exec->propertyNames().underscoreProto) { + slot.setValue(prototype()); + return true; + } + + return false; +} + +// It may seem crazy to inline a function this large, especially a virtual function, +// but it makes a big difference to property lookup that derived classes can inline their +// base class call to this. +ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return inlineGetOwnPropertySlot(exec, propertyName, slot); +} + +ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (!structure()->typeInfo().overridesGetOwnPropertySlot()) + return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); + return getOwnPropertySlot(exec, propertyName, slot); +} + +// It may seem crazy to inline a function this large but it makes a big difference +// since this is function very hot in variable lookup +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSObject* object = this; + while (true) { + if (object->fastGetOwnPropertySlot(exec, propertyName, slot)) + return true; + JSValue prototype = object->prototype(); + if (!prototype.isObject()) + return false; + object = asObject(prototype); + } +} + +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSObject* object = this; + while (true) { + if (object->getOwnPropertySlot(exec, propertyName, slot)) + return true; + JSValue prototype = object->prototype(); + if (!prototype.isObject()) + return false; + object = asObject(prototype); + } +} + +inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const +{ + PropertySlot slot(this); + if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + + return jsUndefined(); +} + +inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot(this); + if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + + return jsUndefined(); +} + +inline bool JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) +{ + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + if (m_structure->isDictionary()) { + unsigned currentAttributes; + JSCell* currentSpecificFunction; + size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); + if (offset != WTF::notFound) { + // If there is currently a specific function, and there now either isn't, + // or the new value is different, then despecify. + if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) + m_structure->despecifyDictionaryFunction(propertyName); + if (checkReadOnly && currentAttributes & ReadOnly) + return false; + + putDirectOffset(offset, value); + // At this point, the objects structure only has a specific value set if previously there + // had been one set, and if the new value being specified is the same (otherwise we would + // have despecified, above). So, if currentSpecificFunction is not set, or if the new + // value is different (or there is no new value), then the slot now has no value - and + // as such it is cachable. + // If there was previously a value, and the new value is the same, then we cannot cache. + if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) + slot.setExistingProperty(this, offset); + return true; + } + + size_t currentCapacity = m_structure->propertyStorageCapacity(); + offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction); + if (currentCapacity != m_structure->propertyStorageCapacity()) + allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); + + ASSERT(offset < m_structure->propertyStorageCapacity()); + putDirectOffset(offset, value); + // See comment on setNewProperty call below. + if (!specificFunction) + slot.setNewProperty(this, offset); + return true; + } + + size_t offset; + size_t currentCapacity = m_structure->propertyStorageCapacity(); + if (RefPtr<Structure> structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) { + if (currentCapacity != structure->propertyStorageCapacity()) + allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); + + ASSERT(offset < structure->propertyStorageCapacity()); + setStructure(structure.release()); + putDirectOffset(offset, value); + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. + if (!specificFunction) + slot.setNewProperty(this, offset); + return true; + } + + unsigned currentAttributes; + JSCell* currentSpecificFunction; + offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); + if (offset != WTF::notFound) { + if (checkReadOnly && currentAttributes & ReadOnly) + return false; + + // There are three possibilities here: + // (1) There is an existing specific value set, and we're overwriting with *the same value*. + // * Do nothing - no need to despecify, but that means we can't cache (a cached + // put could write a different value). Leave the slot in an uncachable state. + // (2) There is a specific value currently set, but we're writing a different value. + // * First, we have to despecify. Having done so, this is now a regular slot + // with no specific value, so go ahead & cache like normal. + // (3) Normal case, there is no specific value set. + // * Go ahead & cache like normal. + if (currentSpecificFunction) { + // case (1) Do the put, then return leaving the slot uncachable. + if (specificFunction == currentSpecificFunction) { + putDirectOffset(offset, value); + return true; + } + // case (2) Despecify, fall through to (3). + setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName)); + } + + // case (3) set the slot, do the put, return. + slot.setExistingProperty(this, offset); + putDirectOffset(offset, value); + return true; + } + + // 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()); + + ASSERT(offset < structure->propertyStorageCapacity()); + setStructure(structure.release()); + putDirectOffset(offset, value); + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. + if (!specificFunction) + slot.setNewProperty(this, offset); + return true; +} + +inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + return putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); +} + +inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot; + putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); +} + +inline bool JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + return putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); +} + +inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot; + putDirectInternal(propertyName, value, attributes, false, slot, 0); +} + +inline bool JSObject::putDirect(const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + return putDirectInternal(propertyName, value, 0, false, slot, 0); +} + +inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value); +} + +inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr) +{ + PutPropertySlot slot; + putDirectInternal(propertyName, value, attr, false, slot, value); +} + +inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes) +{ + size_t currentCapacity = m_structure->propertyStorageCapacity(); + size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0); + if (currentCapacity != m_structure->propertyStorageCapacity()) + allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); + putDirectOffset(offset, value); +} + +inline void JSObject::putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attributes) +{ + size_t currentCapacity = m_structure->propertyStorageCapacity(); + size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value); + if (currentCapacity != m_structure->propertyStorageCapacity()) + allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); + putDirectOffset(offset, value); +} + +inline void JSObject::transitionTo(Structure* newStructure) +{ + if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) + allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); + setStructure(newStructure); +} + +inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +{ + return defaultValue(exec, preferredType); +} + +inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const +{ + PropertySlot slot(asValue()); + return get(exec, propertyName, slot); +} + +inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const +{ + if (UNLIKELY(!isCell())) { + JSObject* prototype = synthesizePrototype(exec); + if (propertyName == exec->propertyNames().underscoreProto) + return prototype; + if (!prototype->getPropertySlot(exec, propertyName, slot)) + return jsUndefined(); + return slot.getValue(exec, propertyName); + } + JSCell* cell = asCell(); + while (true) { + if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + JSValue prototype = asObject(cell)->prototype(); + if (!prototype.isObject()) + return jsUndefined(); + cell = asObject(prototype); + } +} + +inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot(asValue()); + return get(exec, propertyName, slot); +} + +inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const +{ + if (UNLIKELY(!isCell())) { + JSObject* prototype = synthesizePrototype(exec); + if (!prototype->getPropertySlot(exec, propertyName, slot)) + return jsUndefined(); + return slot.getValue(exec, propertyName); + } + JSCell* cell = const_cast<JSCell*>(asCell()); + while (true) { + if (cell->getOwnPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + JSValue prototype = asObject(cell)->prototype(); + if (!prototype.isObject()) + return jsUndefined(); + cell = prototype.asCell(); + } +} + +inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + if (UNLIKELY(!isCell())) { + synthesizeObject(exec)->put(exec, propertyName, value, slot); + return; + } + asCell()->put(exec, propertyName, value, slot); +} + +inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(isCell() && isObject()); + if (!asObject(asCell())->putDirect(propertyName, value, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + +inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) +{ + if (UNLIKELY(!isCell())) { + synthesizeObject(exec)->put(exec, propertyName, value); + return; + } + asCell()->put(exec, propertyName, value); +} + +ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize) +{ + ASSERT(newSize > oldSize); + + // It's important that this function not rely on m_structure, since + // we might be in the middle of a transition. + bool wasInline = (oldSize == JSObject::inlineStorageCapacity); + + PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage); + PropertyStorage newPropertyStorage = new EncodedJSValue[newSize]; + + for (unsigned i = 0; i < oldSize; ++i) + newPropertyStorage[i] = oldPropertyStorage[i]; + + if (!wasInline) + delete [] oldPropertyStorage; + + m_externalStorage = newPropertyStorage; +} + +ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) +{ + JSCell::markChildren(markStack); + + markStack.append(prototype()); + + PropertyStorage storage = propertyStorage(); + size_t storageSize = m_structure->propertyStorageSize(); + markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize); +} + +// --- JSValue inlines ---------------------------- + +ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const +{ + return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec); +} + +inline JSString* JSValue::toThisJSString(ExecState* exec) const +{ + return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec)); +} + +inline JSValue JSValue::toStrictThisObject(ExecState* exec) const +{ + if (!isObject()) + return *this; + return asObject(asCell())->toStrictThisObject(exec); +} + +} // namespace JSC + +#endif // JSObject_h diff --git a/Source/JavaScriptCore/runtime/JSObjectWithGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSObjectWithGlobalObject.cpp new file mode 100644 index 0000000..e9d6c96 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObjectWithGlobalObject.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "JSObjectWithGlobalObject.h" + +#include "JSGlobalObject.h" + +namespace JSC { + +JSObjectWithGlobalObject::JSObjectWithGlobalObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSObject(structure) +{ + COMPILE_ASSERT(AnonymousSlotCount == 1, AnonymousSlotCount_must_be_one); + ASSERT(!globalObject || globalObject->isGlobalObject()); + putAnonymousValue(GlobalObjectSlot, globalObject); +} + +JSGlobalObject* JSObjectWithGlobalObject::globalObject() const +{ + return asGlobalObject((getAnonymousValue(GlobalObjectSlot).asCell())); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSObjectWithGlobalObject.h b/Source/JavaScriptCore/runtime/JSObjectWithGlobalObject.h new file mode 100644 index 0000000..9416a62 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObjectWithGlobalObject.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 JSObjectWithGlobalObject_h +#define JSObjectWithGlobalObject_h + +#include "JSObject.h" + +namespace JSC { + +class JSGlobalObject; + +class JSObjectWithGlobalObject : public JSObject { +public: + static PassRefPtr<Structure> createStructure(JSValue proto) + { + return Structure::create(proto, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + JSGlobalObject* globalObject() const; + +protected: + JSObjectWithGlobalObject(JSGlobalObject*, NonNullPassRefPtr<Structure>); + + JSObjectWithGlobalObject(NonNullPassRefPtr<Structure> structure) + : JSObject(structure) + { + // Should only be used by JSFunction when we aquire the JSFunction vptr. + } + static const unsigned AnonymousSlotCount = JSObject::AnonymousSlotCount + 1; + static const unsigned GlobalObjectSlot = 0; +}; + +} // namespace JSC + +#endif // JSObjectWithGlobalObject_h diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp new file mode 100644 index 0000000..a5d4da0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -0,0 +1,108 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JSPropertyNameIterator.h" + +#include "JSGlobalObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSPropertyNameIterator); + +inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlots) + : JSCell(exec->globalData().propertyNameIteratorStructure.get()) + , m_cachedStructure(0) + , m_numCacheableSlots(numCacheableSlots) + , m_jsStringsSize(propertyNameArrayData->propertyNameVector().size()) + , m_jsStrings(new JSValue[m_jsStringsSize]) +{ + PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArrayData->propertyNameVector(); + for (size_t i = 0; i < m_jsStringsSize; ++i) + m_jsStrings[i] = jsOwnedString(exec, propertyNameVector[i].ustring()); +} + +JSPropertyNameIterator::~JSPropertyNameIterator() +{ + if (m_cachedStructure) + m_cachedStructure->clearEnumerationCache(this); +} + +JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSObject* o) +{ + ASSERT(!o->structure()->enumerationCache() || + o->structure()->enumerationCache()->cachedStructure() != o->structure() || + o->structure()->enumerationCache()->cachedPrototypeChain() != o->structure()->prototypeChain(exec)); + + PropertyNameArray propertyNames(exec); + o->getPropertyNames(exec, propertyNames); + size_t numCacheableSlots = 0; + if (!o->structure()->hasNonEnumerableProperties() && !o->structure()->hasAnonymousSlots() && + !o->structure()->hasGetterSetterProperties() && !o->structure()->isUncacheableDictionary() && + !o->structure()->typeInfo().overridesGetPropertyNames()) + numCacheableSlots = o->structure()->propertyStorageSize(); + + JSPropertyNameIterator* jsPropertyNameIterator = new (exec) JSPropertyNameIterator(exec, propertyNames.data(), numCacheableSlots); + + if (o->structure()->isDictionary()) + return jsPropertyNameIterator; + + if (o->structure()->typeInfo().overridesGetPropertyNames()) + return jsPropertyNameIterator; + + size_t count = normalizePrototypeChain(exec, o); + StructureChain* structureChain = o->structure()->prototypeChain(exec); + RefPtr<Structure>* structure = structureChain->head(); + for (size_t i = 0; i < count; ++i) { + if (structure[i]->typeInfo().overridesGetPropertyNames()) + return jsPropertyNameIterator; + } + + jsPropertyNameIterator->setCachedPrototypeChain(structureChain); + jsPropertyNameIterator->setCachedStructure(o->structure()); + o->structure()->setEnumerationCache(jsPropertyNameIterator); + return jsPropertyNameIterator; +} + +JSValue JSPropertyNameIterator::get(ExecState* exec, JSObject* base, size_t i) +{ + JSValue& identifier = m_jsStrings[i]; + if (m_cachedStructure == base->structure() && m_cachedPrototypeChain == base->structure()->prototypeChain(exec)) + return identifier; + + if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value(exec)))) + return JSValue(); + return identifier; +} + +void JSPropertyNameIterator::markChildren(MarkStack& markStack) +{ + markStack.appendValues(m_jsStrings.get(), m_jsStringsSize, MayContainNullValues); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h new file mode 100644 index 0000000..01700ac --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -0,0 +1,109 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 JSPropertyNameIterator_h +#define JSPropertyNameIterator_h + +#include "JSObject.h" +#include "JSString.h" +#include "Operations.h" +#include "PropertyNameArray.h" + +namespace JSC { + + class Identifier; + class JSObject; + + class JSPropertyNameIterator : public JSCell { + friend class JIT; + + public: + static JSPropertyNameIterator* create(ExecState*, JSObject*); + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(CompoundType, OverridesMarkChildren), AnonymousSlotCount); + } + + virtual ~JSPropertyNameIterator(); + + virtual bool isPropertyNameIterator() const { return true; } + + virtual void markChildren(MarkStack&); + + bool getOffset(size_t i, int& offset) + { + if (i >= m_numCacheableSlots) + return false; + offset = i; + return true; + } + + JSValue get(ExecState*, JSObject*, size_t i); + size_t size() { return m_jsStringsSize; } + + void setCachedStructure(Structure* structure) + { + ASSERT(!m_cachedStructure); + ASSERT(structure); + m_cachedStructure = structure; + } + Structure* cachedStructure() { return m_cachedStructure.get(); } + + void setCachedPrototypeChain(NonNullPassRefPtr<StructureChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; } + StructureChain* cachedPrototypeChain() { return m_cachedPrototypeChain.get(); } + + private: + JSPropertyNameIterator(ExecState*, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlot); + + RefPtr<Structure> m_cachedStructure; + RefPtr<StructureChain> m_cachedPrototypeChain; + uint32_t m_numCacheableSlots; + uint32_t m_jsStringsSize; + OwnArrayPtr<JSValue> m_jsStrings; + }; + + inline void Structure::setEnumerationCache(JSPropertyNameIterator* enumerationCache) + { + ASSERT(!isDictionary()); + m_enumerationCache = enumerationCache; + } + + inline void Structure::clearEnumerationCache(JSPropertyNameIterator* enumerationCache) + { + m_enumerationCache.clear(enumerationCache); + } + + inline JSPropertyNameIterator* Structure::enumerationCache() + { + return m_enumerationCache.get(); + } + +} // namespace JSC + +#endif // JSPropertyNameIterator_h diff --git a/Source/JavaScriptCore/runtime/JSStaticScopeObject.cpp b/Source/JavaScriptCore/runtime/JSStaticScopeObject.cpp new file mode 100644 index 0000000..7ab1d1c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStaticScopeObject.cpp @@ -0,0 +1,82 @@ +/* + * 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 + * 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 "JSStaticScopeObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSStaticScopeObject); + +void JSStaticScopeObject::markChildren(MarkStack& markStack) +{ + JSVariableObject::markChildren(markStack); + markStack.append(d()->registerStore.jsValue()); +} + +JSObject* JSStaticScopeObject::toThisObject(ExecState* exec) const +{ + return exec->globalThisValue(); +} + +JSValue JSStaticScopeObject::toStrictThisObject(ExecState*) const +{ + return jsNull(); +} + +void JSStaticScopeObject::put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&) +{ + if (symbolTablePut(propertyName, value)) + return; + + ASSERT_NOT_REACHED(); +} + +void JSStaticScopeObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + if (symbolTablePutWithAttributes(propertyName, value, attributes)) + return; + + ASSERT_NOT_REACHED(); +} + +bool JSStaticScopeObject::isDynamicScope(bool&) const +{ + return false; +} + +JSStaticScopeObject::~JSStaticScopeObject() +{ + ASSERT(d()); + delete d(); +} + +inline bool JSStaticScopeObject::getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot& slot) +{ + return symbolTableGet(propertyName, slot); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSStaticScopeObject.h b/Source/JavaScriptCore/runtime/JSStaticScopeObject.h new file mode 100644 index 0000000..e69356a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStaticScopeObject.h @@ -0,0 +1,72 @@ +/* + * 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 + * 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 JSStaticScopeObject_h +#define JSStaticScopeObject_h + +#include "JSVariableObject.h" + +namespace JSC{ + + class JSStaticScopeObject : public JSVariableObject { + protected: + using JSVariableObject::JSVariableObjectData; + struct JSStaticScopeObjectData : public JSVariableObjectData { + JSStaticScopeObjectData() + : JSVariableObjectData(&symbolTable, ®isterStore + 1) + { + } + SymbolTable symbolTable; + Register registerStore; + }; + + public: + JSStaticScopeObject(ExecState* exec, const Identifier& ident, JSValue value, unsigned attributes) + : JSVariableObject(exec->globalData().staticScopeStructure, new JSStaticScopeObjectData()) + { + d()->registerStore = value; + symbolTable().add(ident.impl(), SymbolTableEntry(-1, attributes)); + } + virtual ~JSStaticScopeObject(); + virtual void markChildren(MarkStack&); + bool isDynamicScope(bool& requiresDynamicChecks) const; + virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); + void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes); + + static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | NeedsThisConversion | OverridesMarkChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; + + private: + JSStaticScopeObjectData* d() { return static_cast<JSStaticScopeObjectData*>(JSVariableObject::d); } + }; + +} + +#endif // JSStaticScopeObject_h diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp new file mode 100644 index 0000000..ba28139 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSString.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 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 + * 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 "JSString.h" + +#include "JSGlobalObject.h" +#include "JSGlobalObjectFunctions.h" +#include "JSObject.h" +#include "Operations.h" +#include "StringObject.h" +#include "StringPrototype.h" + +namespace JSC { + +static const unsigned substringFromRopeCutoff = 4; + +// Overview: this methods converts a JSString from holding a string in rope form +// down to a simple UString representation. It does so by building up the string +// backwards, since we want to avoid recursion, we expect that the tree structure +// representing the rope is likely imbalanced with more nodes down the left side +// (since appending to the string is likely more common) - and as such resolving +// in this fashion should minimize work queue size. (If we built the queue forwards +// we would likely have to place all of the constituent StringImpls into the +// Vector before performing any concatenation, but by working backwards we likely +// only fill the queue with the number of substrings at any given level in a +// rope-of-ropes.) +void JSString::resolveRope(ExecState* exec) const +{ + ASSERT(isRope()); + + // Allocate the buffer to hold the final string, position initially points to the end. + UChar* buffer; + if (PassRefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) + m_value = newImpl; + else { + for (unsigned i = 0; i < m_fiberCount; ++i) { + RopeImpl::deref(m_other.m_fibers[i]); + m_other.m_fibers[i] = 0; + } + m_fiberCount = 0; + ASSERT(!isRope()); + ASSERT(m_value == UString()); + if (exec) + throwOutOfMemoryError(exec); + return; + } + UChar* position = buffer + m_length; + + // Start with the current RopeImpl. + Vector<RopeImpl::Fiber, 32> workQueue; + RopeImpl::Fiber currentFiber; + for (unsigned i = 0; i < (m_fiberCount - 1); ++i) + workQueue.append(m_other.m_fibers[i]); + currentFiber = m_other.m_fibers[m_fiberCount - 1]; + while (true) { + if (RopeImpl::isRope(currentFiber)) { + RopeImpl* rope = static_cast<RopeImpl*>(currentFiber); + // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber' + // (we will be working backwards over the rope). + unsigned fiberCountMinusOne = rope->fiberCount() - 1; + for (unsigned i = 0; i < fiberCountMinusOne; ++i) + workQueue.append(rope->fibers()[i]); + currentFiber = rope->fibers()[fiberCountMinusOne]; + } else { + StringImpl* string = static_cast<StringImpl*>(currentFiber); + unsigned length = string->length(); + position -= length; + StringImpl::copyChars(position, string->characters(), length); + + // Was this the last item in the work queue? + if (workQueue.isEmpty()) { + // Create a string from the UChar buffer, clear the rope RefPtr. + ASSERT(buffer == position); + for (unsigned i = 0; i < m_fiberCount; ++i) { + RopeImpl::deref(m_other.m_fibers[i]); + m_other.m_fibers[i] = 0; + } + m_fiberCount = 0; + + ASSERT(!isRope()); + return; + } + + // No! - set the next item up to process. + currentFiber = workQueue.last(); + workQueue.removeLast(); + } + } +} + +// This function construsts a substring out of a rope without flattening by reusing the existing fibers. +// This can reduce memory usage substantially. Since traversing ropes is slow the function will revert +// back to flattening if the rope turns out to be long. +JSString* JSString::substringFromRope(ExecState* exec, unsigned substringStart, unsigned substringLength) +{ + ASSERT(isRope()); + ASSERT(substringLength); + + JSGlobalData* globalData = &exec->globalData(); + + UString substringFibers[3]; + + unsigned fiberCount = 0; + unsigned substringFiberCount = 0; + unsigned substringEnd = substringStart + substringLength; + unsigned fiberEnd = 0; + + RopeIterator end; + for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) { + ++fiberCount; + StringImpl* fiberString = *it; + unsigned fiberStart = fiberEnd; + fiberEnd = fiberStart + fiberString->length(); + if (fiberEnd <= substringStart) + continue; + unsigned copyStart = std::max(substringStart, fiberStart); + unsigned copyEnd = std::min(substringEnd, fiberEnd); + if (copyStart == fiberStart && copyEnd == fiberEnd) + substringFibers[substringFiberCount++] = UString(fiberString); + else + substringFibers[substringFiberCount++] = UString(StringImpl::create(fiberString, copyStart - fiberStart, copyEnd - copyStart)); + if (fiberEnd >= substringEnd) + break; + if (fiberCount > substringFromRopeCutoff || substringFiberCount >= 3) { + // This turned out to be a really inefficient rope. Just flatten it. + resolveRope(exec); + return jsSubstring(&exec->globalData(), m_value, substringStart, substringLength); + } + } + ASSERT(substringFiberCount && substringFiberCount <= 3); + + if (substringLength == 1) { + ASSERT(substringFiberCount == 1); + UChar c = substringFibers[0].characters()[0]; + if (c <= 0xFF) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + if (substringFiberCount == 1) + return new (globalData) JSString(globalData, substringFibers[0]); + if (substringFiberCount == 2) + return new (globalData) JSString(globalData, substringFibers[0], substringFibers[1]); + return new (globalData) JSString(globalData, substringFibers[0], substringFibers[1], substringFibers[2]); +} + +JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement) +{ + if (!isRope()) { + size_t matchPosition = m_value.find(character); + if (matchPosition == notFound) + return JSValue(this); + return jsString(exec, m_value.substringSharingImpl(0, matchPosition), replacement, m_value.substringSharingImpl(matchPosition + 1)); + } + + RopeIterator end; + + // Count total fibers and find matching string. + size_t fiberCount = 0; + StringImpl* matchString = 0; + size_t matchPosition = notFound; + for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) { + ++fiberCount; + if (matchString) + continue; + + StringImpl* string = *it; + matchPosition = string->find(character); + if (matchPosition == notFound) + continue; + matchString = string; + } + + if (!matchString) + return this; + + RopeBuilder builder(replacement.length() ? fiberCount + 2 : fiberCount + 1); + if (UNLIKELY(builder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + + for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) { + StringImpl* string = *it; + if (string != matchString) { + builder.append(UString(string)); + continue; + } + + builder.append(UString(string).substringSharingImpl(0, matchPosition)); + if (replacement.length()) + builder.append(replacement); + builder.append(UString(string).substringSharingImpl(matchPosition + 1)); + matchString = 0; + } + + JSGlobalData* globalData = &exec->globalData(); + return JSValue(new (globalData) JSString(globalData, builder.release())); +} + +JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i) +{ + ASSERT(isRope()); + resolveRope(exec); + // Return a safe no-value result, this should never be used, since the excetion will be thrown. + if (exec->exception()) + return jsString(exec, ""); + ASSERT(!isRope()); + ASSERT(i < m_value.length()); + return jsSingleCharacterSubstring(exec, m_value, i); +} + +JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const +{ + return const_cast<JSString*>(this); +} + +bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) +{ + result = this; + number = jsToNumber(value(exec)); + return false; +} + +bool JSString::toBoolean(ExecState*) const +{ + return m_length; +} + +double JSString::toNumber(ExecState* exec) const +{ + return jsToNumber(value(exec)); +} + +UString JSString::toString(ExecState* exec) const +{ + return value(exec); +} + +inline StringObject* StringObject::create(ExecState* exec, JSString* string) +{ + return new (exec) StringObject(exec->lexicalGlobalObject()->stringObjectStructure(), string); +} + +JSObject* JSString::toObject(ExecState* exec) const +{ + return StringObject::create(exec, const_cast<JSString*>(this)); +} + +JSObject* JSString::toThisObject(ExecState* exec) const +{ + return StringObject::create(exec, const_cast<JSString*>(this)); +} + +bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + // The semantics here are really getPropertySlot, not getOwnPropertySlot. + // This function should only be called by JSValue::get. + if (getStringPropertySlot(exec, propertyName, slot)) + return true; + if (propertyName == exec->propertyNames().underscoreProto) { + slot.setValue(exec->lexicalGlobalObject()->stringPrototype()); + return true; + } + slot.setBase(this); + JSObject* object; + for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) { + object = asObject(prototype); + if (object->getOwnPropertySlot(exec, propertyName, slot)) + return true; + } + slot.setUndefined(); + return true; +} + +bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly); + return true; + } + + bool isStrictUInt32; + unsigned i = propertyName.toUInt32(isStrictUInt32); + if (isStrictUInt32 && i < m_length) { + descriptor.setDescriptor(getIndex(exec, 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. + // This function should only be called by JSValue::get. + if (getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSString::getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h new file mode 100644 index 0000000..fefffde --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -0,0 +1,647 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 + * 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. + * + */ + +#ifndef JSString_h +#define JSString_h + +#include "CallFrame.h" +#include "CommonIdentifiers.h" +#include "Identifier.h" +#include "JSNumberCell.h" +#include "PropertyDescriptor.h" +#include "PropertySlot.h" +#include "RopeImpl.h" + +namespace JSC { + + class JSString; + + JSString* jsEmptyString(JSGlobalData*); + JSString* jsEmptyString(ExecState*); + JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string + JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string + + JSString* jsSingleCharacterString(JSGlobalData*, UChar); + JSString* jsSingleCharacterString(ExecState*, UChar); + JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset); + JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length); + JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length); + + // Non-trivial strings are two or more characters long. + // These functions are faster than just calling jsString. + JSString* jsNontrivialString(JSGlobalData*, const UString&); + JSString* jsNontrivialString(ExecState*, const UString&); + JSString* jsNontrivialString(JSGlobalData*, const char*); + JSString* jsNontrivialString(ExecState*, const char*); + + // Should be used for strings that are owned by an object that will + // likely outlive the JSValue this makes, such as the parse tree or a + // DOM object that contains a UString + JSString* jsOwnedString(JSGlobalData*, const UString&); + JSString* jsOwnedString(ExecState*, const UString&); + + typedef void (*JSStringFinalizerCallback)(JSString*, void* context); + JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context); + + class JS_EXPORTCLASS JSString : public JSCell { + public: + friend class JIT; + friend class JSGlobalData; + friend class SpecializedThunkJIT; + friend struct ThunkHelpers; + + class RopeBuilder { + public: + RopeBuilder(unsigned fiberCount) + : m_index(0) + , m_rope(RopeImpl::tryCreateUninitialized(fiberCount)) + { + } + + bool isOutOfMemory() { return !m_rope; } + + void append(RopeImpl::Fiber& fiber) + { + ASSERT(m_rope); + m_rope->initializeFiber(m_index, fiber); + } + void append(const UString& string) + { + ASSERT(m_rope); + m_rope->initializeFiber(m_index, string.impl()); + } + void append(JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_fiberCount; ++i) + append(jsString->m_other.m_fibers[i]); + } else + append(jsString->string()); + } + + PassRefPtr<RopeImpl> release() + { + ASSERT(m_index == m_rope->fiberCount()); + return m_rope.release(); + } + + unsigned length() { return m_rope->length(); } + + private: + unsigned m_index; + RefPtr<RopeImpl> m_rope; + }; + + class RopeIterator { + public: + RopeIterator() { } + + RopeIterator(RopeImpl::Fiber* fibers, size_t fiberCount) + { + ASSERT(fiberCount); + m_workQueue.append(WorkItem(fibers, fiberCount)); + skipRopes(); + } + + RopeIterator& operator++() + { + WorkItem& item = m_workQueue.last(); + ASSERT(!RopeImpl::isRope(item.fibers[item.i])); + if (++item.i == item.fiberCount) + m_workQueue.removeLast(); + skipRopes(); + return *this; + } + + StringImpl* operator*() + { + WorkItem& item = m_workQueue.last(); + RopeImpl::Fiber fiber = item.fibers[item.i]; + ASSERT(!RopeImpl::isRope(fiber)); + return static_cast<StringImpl*>(fiber); + } + + bool operator!=(const RopeIterator& other) const + { + return m_workQueue != other.m_workQueue; + } + + private: + struct WorkItem { + WorkItem(RopeImpl::Fiber* fibers, size_t fiberCount) + : fibers(fibers) + , fiberCount(fiberCount) + , i(0) + { + } + + bool operator!=(const WorkItem& other) const + { + return fibers != other.fibers || fiberCount != other.fiberCount || i != other.i; + } + + RopeImpl::Fiber* fibers; + size_t fiberCount; + size_t i; + }; + + void skipRopes() + { + if (m_workQueue.isEmpty()) + return; + + while (1) { + WorkItem& item = m_workQueue.last(); + RopeImpl::Fiber fiber = item.fibers[item.i]; + if (!RopeImpl::isRope(fiber)) + break; + RopeImpl* rope = static_cast<RopeImpl*>(fiber); + if (++item.i == item.fiberCount) + m_workQueue.removeLast(); + m_workQueue.append(WorkItem(rope->fibers(), rope->fiberCount())); + } + } + + Vector<WorkItem, 16> m_workQueue; + }; + + ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value) + : JSCell(globalData->stringStructure.get()) + , m_length(value.length()) + , m_value(value) + , m_fiberCount(0) + { + ASSERT(!m_value.isNull()); + Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost()); + } + + enum HasOtherOwnerType { HasOtherOwner }; + JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType) + : JSCell(globalData->stringStructure.get()) + , m_length(value.length()) + , m_value(value) + , m_fiberCount(0) + { + ASSERT(!m_value.isNull()); + } + JSString(JSGlobalData* globalData, PassRefPtr<StringImpl> value, HasOtherOwnerType) + : JSCell(globalData->stringStructure.get()) + , m_length(value->length()) + , m_value(value) + , m_fiberCount(0) + { + ASSERT(!m_value.isNull()); + } + JSString(JSGlobalData* globalData, PassRefPtr<RopeImpl> rope) + : JSCell(globalData->stringStructure.get()) + , m_length(rope->length()) + , m_fiberCount(1) + { + m_other.m_fibers[0] = rope.leakRef(); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2) + : JSCell(globalData->stringStructure.get()) + , m_length(s1->length() + s2->length()) + , m_fiberCount(fiberCount) + { + ASSERT(fiberCount <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, s1); + appendStringInConstruct(index, s2); + ASSERT(fiberCount == index); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2) + : JSCell(globalData->stringStructure.get()) + , m_length(s1->length() + u2.length()) + , m_fiberCount(fiberCount) + { + ASSERT(fiberCount <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, s1); + appendStringInConstruct(index, u2); + ASSERT(fiberCount == index); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2) + : JSCell(globalData->stringStructure.get()) + , m_length(u1.length() + s2->length()) + , m_fiberCount(fiberCount) + { + ASSERT(fiberCount <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, s2); + ASSERT(fiberCount == index); + } + // This constructor constructs a new string by concatenating v1, v2 & v3. + // This should only be called with fiberCount <= 3 ... which since every + // value must require a fiberCount of at least one implies that the length + // for each value must be exactly 1! + JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3) + : JSCell(exec->globalData().stringStructure.get()) + , m_length(0) + , m_fiberCount(s_maxInternalRopeLength) + { + unsigned index = 0; + appendValueInConstructAndIncrementLength(exec, index, v1); + appendValueInConstructAndIncrementLength(exec, index, v2); + appendValueInConstructAndIncrementLength(exec, index, v3); + ASSERT(index == s_maxInternalRopeLength); + } + + // This constructor constructs a new string by concatenating u1 & u2. + JSString(JSGlobalData* globalData, const UString& u1, const UString& u2) + : JSCell(globalData->stringStructure.get()) + , m_length(u1.length() + u2.length()) + , m_fiberCount(2) + { + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, u2); + ASSERT(index <= s_maxInternalRopeLength); + } + + // This constructor constructs a new string by concatenating u1, u2 & u3. + JSString(JSGlobalData* globalData, const UString& u1, const UString& u2, const UString& u3) + : JSCell(globalData->stringStructure.get()) + , m_length(u1.length() + u2.length() + u3.length()) + , m_fiberCount(s_maxInternalRopeLength) + { + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, u2); + appendStringInConstruct(index, u3); + ASSERT(index <= s_maxInternalRopeLength); + } + + JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context) + : JSCell(globalData->stringStructure.get()) + , m_length(value.length()) + , m_value(value) + , m_fiberCount(0) + { + ASSERT(!m_value.isNull()); + // nasty hack because we can't union non-POD types + m_other.m_finalizerCallback = finalizer; + m_other.m_finalizerContext = context; + Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost()); + } + + ~JSString() + { + ASSERT(vptr() == JSGlobalData::jsStringVPtr); + for (unsigned i = 0; i < m_fiberCount; ++i) + RopeImpl::deref(m_other.m_fibers[i]); + + if (!m_fiberCount && m_other.m_finalizerCallback) + m_other.m_finalizerCallback(this, m_other.m_finalizerContext); + } + + const UString& value(ExecState* exec) const + { + if (isRope()) + resolveRope(exec); + return m_value; + } + const UString& tryGetValue() const + { + if (isRope()) + resolveRope(0); + return m_value; + } + unsigned length() { return m_length; } + + 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 < m_length; } + JSString* getIndex(ExecState*, unsigned); + JSString* getIndexSlowCase(ExecState*, unsigned); + + JSValue replaceCharacter(ExecState*, UChar, const UString& replacement); + + static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); } + + private: + enum VPtrStealingHackType { VPtrStealingHack }; + JSString(VPtrStealingHackType) + : JSCell(0) + , m_fiberCount(0) + { + } + + void resolveRope(ExecState*) const; + JSString* substringFromRope(ExecState*, unsigned offset, unsigned length); + + void appendStringInConstruct(unsigned& index, const UString& string) + { + StringImpl* impl = string.impl(); + impl->ref(); + m_other.m_fibers[index++] = impl; + } + + void appendStringInConstruct(unsigned& index, JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_fiberCount; ++i) { + RopeImpl::Fiber fiber = jsString->m_other.m_fibers[i]; + fiber->ref(); + m_other.m_fibers[index++] = fiber; + } + } else + appendStringInConstruct(index, jsString->string()); + } + + void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v) + { + if (v.isString()) { + ASSERT(v.asCell()->isString()); + JSString* s = static_cast<JSString*>(v.asCell()); + ASSERT(s->fiberCount() == 1); + appendStringInConstruct(index, s); + m_length += s->length(); + } else { + UString u(v.toString(exec)); + StringImpl* impl = u.impl(); + impl->ref(); + m_other.m_fibers[index++] = impl; + m_length += u.length(); + } + } + + 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 JSObject* toObject(ExecState*) const; + virtual UString toString(ExecState*) const; + + virtual JSObject* toThisObject(ExecState*) const; + + // 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&); + + static const unsigned s_maxInternalRopeLength = 3; + + // A string is represented either by a UString or a RopeImpl. + unsigned m_length; + mutable UString m_value; + mutable unsigned m_fiberCount; + // This structure exists to support a temporary workaround for a GC issue. + struct JSStringFinalizerStruct { + JSStringFinalizerStruct() : m_finalizerCallback(0) {} + union { + mutable FixedArray<RopeImpl::Fiber, s_maxInternalRopeLength> m_fibers; + struct { + JSStringFinalizerCallback m_finalizerCallback; + void* m_finalizerContext; + }; + }; + } m_other; + + bool isRope() const { return m_fiberCount; } + UString& string() { ASSERT(!isRope()); return m_value; } + unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; } + + friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2); + friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2); + friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2); + friend JSValue jsString(ExecState* exec, Register* strings, unsigned count); + friend JSValue jsString(ExecState* exec, JSValue thisValue); + friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context); + friend JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length); + }; + + JSString* asString(JSValue); + + // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor, + // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>. + // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one. + // The below function must be called by any inline function that invokes a JSString constructor. +#if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore) + inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; } +#else + inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; } +#endif + + inline JSString* asString(JSValue value) + { + ASSERT(value.asCell()->isString()); + return static_cast<JSString*>(value.asCell()); + } + + inline JSString* jsEmptyString(JSGlobalData* globalData) + { + return globalData->smallStrings.emptyString(globalData); + } + + inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c) + { + if (c <= 0xFF) + return globalData->smallStrings.singleCharacterString(globalData, c); + return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1))); + } + + inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) + { + JSGlobalData* globalData = &exec->globalData(); + ASSERT(offset < static_cast<unsigned>(s.length())); + UChar c = s.characters()[offset]; + if (c <= 0xFF) + return globalData->smallStrings.singleCharacterString(globalData, c); + return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, 1)))); + } + + inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) + { + ASSERT(s); + ASSERT(s[0]); + ASSERT(s[1]); + return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); + } + + inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s) + { + ASSERT(s.length() > 1); + return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); + } + + inline JSString* JSString::getIndex(ExecState* exec, unsigned i) + { + ASSERT(canGetIndex(i)); + if (isRope()) + return getIndexSlowCase(exec, i); + ASSERT(i < m_value.length()); + return jsSingleCharacterSubstring(exec, m_value, i); + } + + inline JSString* jsString(JSGlobalData* globalData, const UString& s) + { + int size = s.length(); + if (!size) + return globalData->smallStrings.emptyString(globalData); + if (size == 1) { + UChar c = s.characters()[0]; + if (c <= 0xFF) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); + } + + inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context) + { + ASSERT(s.length() && (s.length() > 1 || s.characters()[0] > 0xFF)); + JSGlobalData* globalData = &exec->globalData(); + return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context)); + } + + inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length) + { + ASSERT(offset <= static_cast<unsigned>(s->length())); + ASSERT(length <= static_cast<unsigned>(s->length())); + ASSERT(offset + length <= static_cast<unsigned>(s->length())); + JSGlobalData* globalData = &exec->globalData(); + if (!length) + return globalData->smallStrings.emptyString(globalData); + if (s->isRope()) + return s->substringFromRope(exec, offset, length); + return jsSubstring(globalData, s->m_value, offset, length); + } + + inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length) + { + ASSERT(offset <= static_cast<unsigned>(s.length())); + ASSERT(length <= static_cast<unsigned>(s.length())); + ASSERT(offset + length <= static_cast<unsigned>(s.length())); + if (!length) + return globalData->smallStrings.emptyString(globalData); + if (length == 1) { + UChar c = s.characters()[offset]; + if (c <= 0xFF) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, length)), JSString::HasOtherOwner)); + } + + inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s) + { + int size = s.length(); + if (!size) + return globalData->smallStrings.emptyString(globalData); + if (size == 1) { + UChar c = s.characters()[0]; + if (c <= 0xFF) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner)); + } + + inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); } + inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); } + inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); } + inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); } + inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); } + inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); } + inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); } + + ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) + { + if (propertyName == exec->propertyNames().length) { + slot.setValue(jsNumber(m_length)); + return true; + } + + bool isStrictUInt32; + unsigned i = propertyName.toUInt32(isStrictUInt32); + if (isStrictUInt32 && i < m_length) { + slot.setValue(getIndex(exec, i)); + return true; + } + + return false; + } + + ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) + { + if (propertyName < m_length) { + slot.setValue(getIndex(exec, propertyName)); + return true; + } + + return false; + } + + inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; } + + // --- JSValue inlines ---------------------------- + + inline UString JSValue::toString(ExecState* exec) const + { + if (isString()) + return static_cast<JSString*>(asCell())->value(exec); + 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); + } + + inline UString JSValue::toPrimitiveString(ExecState* exec) const + { + if (isString()) + return static_cast<JSString*>(asCell())->value(exec); + 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()->toPrimitive(exec, NoPreference).toString(exec); + } + +} // namespace JSC + +#endif // JSString_h diff --git a/Source/JavaScriptCore/runtime/JSStringBuilder.h b/Source/JavaScriptCore/runtime/JSStringBuilder.h new file mode 100644 index 0000000..49d4a63 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringBuilder.h @@ -0,0 +1,134 @@ +/* + * 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 JSStringBuilder_h +#define JSStringBuilder_h + +#include "ExceptionHelpers.h" +#include "JSString.h" +#include "UStringConcatenate.h" +#include "Vector.h" + +namespace JSC { + +class JSStringBuilder { +public: + JSStringBuilder() + : m_okay(true) + { + } + + void append(const UChar u) + { + m_okay &= buffer.tryAppend(&u, 1); + } + + void append(const char* str) + { + append(str, strlen(str)); + } + + void append(const char* str, size_t len) + { + m_okay &= buffer.tryReserveCapacity(buffer.size() + len); + for (size_t i = 0; i < len; i++) { + UChar u = static_cast<unsigned char>(str[i]); + m_okay &= buffer.tryAppend(&u, 1); + } + } + + void append(const UChar* str, size_t len) + { + m_okay &= buffer.tryAppend(str, len); + } + + void append(const UString& str) + { + m_okay &= buffer.tryAppend(str.characters(), str.length()); + } + + JSValue build(ExecState* exec) + { + if (!m_okay) + return throwOutOfMemoryError(exec); + buffer.shrinkToFit(); + if (!buffer.data()) + return throwOutOfMemoryError(exec); + return jsString(exec, UString::adopt(buffer)); + } + +protected: + Vector<UChar, 64> buffer; + bool m_okay; +}; + +template<typename StringType1, typename StringType2> +inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2) +{ + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2); + if (!result) + return throwOutOfMemoryError(exec); + return jsNontrivialString(exec, result); +} + +template<typename StringType1, typename StringType2, typename StringType3> +inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3) +{ + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3); + if (!result) + return throwOutOfMemoryError(exec); + return jsNontrivialString(exec, result); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> +inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) +{ + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3, string4); + if (!result) + return throwOutOfMemoryError(exec); + return jsNontrivialString(exec, result); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> +inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) +{ + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3, string4, string5); + if (!result) + return throwOutOfMemoryError(exec); + return jsNontrivialString(exec, result); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> +inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) +{ + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3, string4, string5, string6); + if (!result) + return throwOutOfMemoryError(exec); + return jsNontrivialString(exec, result); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/JSType.h b/Source/JavaScriptCore/runtime/JSType.h new file mode 100644 index 0000000..882b218 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSType.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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 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. + * + */ + +#ifndef JSType_h +#define JSType_h + +namespace JSC { + + /** + * Primitive types + */ + enum JSType { + UnspecifiedType = 0, + UndefinedType = 1, + BooleanType = 2, + NumberType = 3, + NullType = 4, + StringType = 5, + // The CompoundType value must come before any JSType that may have children + CompoundType = 6, + ObjectType = 7, + GetterSetterType = 8 + }; + +} // namespace JSC + +#endif diff --git a/Source/JavaScriptCore/runtime/JSTypeInfo.h b/Source/JavaScriptCore/runtime/JSTypeInfo.h new file mode 100644 index 0000000..e225bc7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypeInfo.h @@ -0,0 +1,80 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 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" + +namespace JSC { + + // WebCore uses MasqueradesAsUndefined to make document.all and style.filter undetectable. + static const unsigned MasqueradesAsUndefined = 1; + static const unsigned ImplementsHasInstance = 1 << 1; + static const unsigned OverridesHasInstance = 1 << 2; + static const unsigned ImplementsDefaultHasInstance = 1 << 3; + static const unsigned NeedsThisConversion = 1 << 4; + static const unsigned OverridesGetOwnPropertySlot = 1 << 5; + static const unsigned OverridesMarkChildren = 1 << 6; + static const unsigned OverridesGetPropertyNames = 1 << 7; + + class TypeInfo { + friend class JIT; + public: + TypeInfo(JSType type, unsigned flags = 0) + : m_type(type) + { + ASSERT(flags <= 0xFF); + ASSERT(type <= 0xFF); + // ImplementsDefaultHasInstance means (ImplementsHasInstance & !OverridesHasInstance) + if ((flags & (ImplementsHasInstance | OverridesHasInstance)) == ImplementsHasInstance) + m_flags = flags | ImplementsDefaultHasInstance; + else + m_flags = flags; + } + + JSType type() const { return (JSType)m_type; } + + bool masqueradesAsUndefined() const { return m_flags & MasqueradesAsUndefined; } + bool implementsHasInstance() const { return m_flags & ImplementsHasInstance; } + bool overridesHasInstance() const { return m_flags & OverridesHasInstance; } + bool needsThisConversion() const { return m_flags & NeedsThisConversion; } + bool overridesGetOwnPropertySlot() const { return m_flags & OverridesGetOwnPropertySlot; } + bool overridesMarkChildren() const { return m_flags & OverridesMarkChildren; } + bool overridesGetPropertyNames() const { return m_flags & OverridesGetPropertyNames; } + unsigned flags() const { return m_flags; } + + private: + unsigned char m_type; + unsigned char m_flags; + }; + +} + +#endif // JSTypeInfo_h diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp new file mode 100644 index 0000000..f4662db --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008 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 + * 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 "JSValue.h" + +#include "BooleanConstructor.h" +#include "BooleanPrototype.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSGlobalObject.h" +#include "JSFunction.h" +#include "JSNotAnObject.h" +#include "NumberObject.h" +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> + +namespace JSC { + +static const double D32 = 4294967296.0; + +// ECMA 9.4 +double JSValue::toInteger(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + double d = toNumber(exec); + return isnan(d) ? 0.0 : trunc(d); +} + +double JSValue::toIntegerPreserveNaN(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + return trunc(toNumber(exec)); +} + +JSObject* JSValue::toObjectSlowCase(ExecState* exec) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, asValue()); + + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return new (exec) JSNotAnObject(exec); +} + +JSObject* JSValue::toThisObjectSlowCase(ExecState* exec) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, asValue()); + ASSERT(isUndefinedOrNull()); + return exec->globalThisValue(); +} + +JSObject* JSValue::synthesizeObject(ExecState* exec) const +{ + ASSERT(!isCell()); + if (isNumber()) + return constructNumber(exec, asValue()); + if (isBoolean()) + return constructBooleanFromImmediateBoolean(exec, asValue()); + + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return new (exec) JSNotAnObject(exec); +} + +JSObject* JSValue::synthesizePrototype(ExecState* exec) const +{ + ASSERT(!isCell()); + if (isNumber()) + return exec->lexicalGlobalObject()->numberPrototype(); + if (isBoolean()) + return exec->lexicalGlobalObject()->booleanPrototype(); + + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return new (exec) JSNotAnObject(exec); +} + +#ifndef NDEBUG +char* JSValue::description() +{ + static const size_t size = 32; + static char description[size]; + + if (!*this) + snprintf(description, size, "<JSValue()>"); + else if (isInt32()) + snprintf(description, size, "Int32: %d", asInt32()); + else if (isDouble()) + snprintf(description, size, "Double: %lf", asDouble()); + else if (isCell()) + snprintf(description, size, "Cell: %p", asCell()); + else if (isTrue()) + snprintf(description, size, "True"); + else if (isFalse()) + snprintf(description, size, "False"); + else if (isNull()) + snprintf(description, size, "Null"); + else if (isUndefined()) + snprintf(description, size, "Undefined"); + else + snprintf(description, size, "INVALID"); + + return description; +} +#endif + +// This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec. +// Note that this operation is identical to ToUInt32 other than to interpretation +// of the resulting bit-pattern (as such this metod is also called to implement +// ToUInt32). +// +// The operation can be descibed as round towards zero, then select the 32 least +// bits of the resulting value in 2s-complement representation. +int32_t toInt32(double number) +{ + int64_t bits = WTF::bitwise_cast<int64_t>(number); + int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff; + + // If exponent < 0 there will be no bits to the left of the decimal point + // after rounding; if the exponent is > 83 then no bits of precision can be + // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits + // of fractional precision). + // Note this case handles 0, -0, and all infinte, NaN, & denormal value. + if (exp < 0 || exp > 83) + return 0; + + // Select the appropriate 32-bits from the floating point mantissa. If the + // exponent is 52 then the bits we need to select are already aligned to the + // lowest bits of the 64-bit integer representation of tghe number, no need + // to shift. If the exponent is greater than 52 we need to shift the value + // left by (exp - 52), if the value is less than 52 we need to shift right + // accordingly. + int32_t result = (exp > 52) + ? static_cast<int32_t>(bits << (exp - 52)) + : static_cast<int32_t>(bits >> (52 - exp)); + + // IEEE-754 double precision values are stored omitting an implicit 1 before + // the decimal point; we need to reinsert this now. We may also the shifted + // invalid bits into the result that are not a part of the mantissa (the sign + // and exponent bits from the floatingpoint representation); mask these out. + if (exp < 32) { + int32_t missingOne = 1 << exp; + result &= missingOne - 1; + result += missingOne; + } + + // If the input value was negative (we could test either 'number' or 'bits', + // but testing 'bits' is likely faster) invert the result appropriately. + return bits < 0 ? -result : result; +} + +NEVER_INLINE double nonInlineNaN() +{ +#if OS(SYMBIAN) + return nanval(); +#else + return std::numeric_limits<double>::quiet_NaN(); +#endif +} + +bool JSValue::isValidCallee() +{ + return asObject(asObject(asCell())->getAnonymousValue(0))->isGlobalObject(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSValue.h b/Source/JavaScriptCore/runtime/JSValue.h new file mode 100644 index 0000000..dc54f40 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSValue.h @@ -0,0 +1,770 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 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 + * 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. + * + */ + +#ifndef JSValue_h +#define JSValue_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> +#include <wtf/MathExtras.h> + +namespace JSC { + + class ExecState; + class Identifier; + class JSCell; + class JSGlobalData; + class JSImmediate; + class JSObject; + class JSString; + class PropertySlot; + class PutPropertySlot; + class UString; + + struct ClassInfo; + struct Instruction; + + enum PreferredPrimitiveType { NoPreference, PreferNumber, PreferString }; + +#if USE(JSVALUE32_64) + typedef int64_t EncodedJSValue; +#else + typedef void* EncodedJSValue; +#endif + + double nonInlineNaN(); + + // This implements ToInt32, defined in ECMA-262 9.5. + int32_t toInt32(double); + + // This implements ToUInt32, defined in ECMA-262 9.6. + inline uint32_t toUInt32(double number) + { + // As commented in the spec, the operation of ToInt32 and ToUint32 only differ + // in how the result is interpreted; see NOTEs in sections 9.5 and 9.6. + return toInt32(number); + } + + class JSValue { + friend class JSImmediate; + friend struct EncodedJSValueHashTraits; + friend class JIT; + friend class JITStubs; + friend class JITStubCall; + friend class JSInterfaceJIT; + friend class SpecializedThunkJIT; + + public: + static EncodedJSValue encode(JSValue value); + static JSValue decode(EncodedJSValue ptr); +#if USE(JSVALUE64) + private: + static JSValue makeImmediate(intptr_t value); + intptr_t immediateValue(); + public: +#endif + enum JSNullTag { JSNull }; + enum JSUndefinedTag { JSUndefined }; + enum JSTrueTag { JSTrue }; + enum JSFalseTag { JSFalse }; + enum EncodeAsDoubleTag { EncodeAsDouble }; + + JSValue(); + JSValue(JSNullTag); + JSValue(JSUndefinedTag); + JSValue(JSTrueTag); + JSValue(JSFalseTag); + JSValue(JSCell* ptr); + JSValue(const JSCell* ptr); + + // Numbers + JSValue(EncodeAsDoubleTag, double); + explicit JSValue(double); + explicit JSValue(char); + explicit JSValue(unsigned char); + explicit JSValue(short); + explicit JSValue(unsigned short); + explicit JSValue(int); + explicit JSValue(unsigned); + explicit JSValue(long); + explicit JSValue(unsigned long); + explicit JSValue(long long); + explicit JSValue(unsigned long long); + + operator bool() const; + bool operator==(const JSValue& other) const; + bool operator!=(const JSValue& other) const; + + bool isInt32() const; + bool isUInt32() const; + bool isDouble() const; + bool isTrue() const; + bool isFalse() const; + + int32_t asInt32() const; + uint32_t asUInt32() const; + double asDouble() const; + + // Querying the type. + bool isUndefined() const; + bool isNull() const; + bool isUndefinedOrNull() const; + bool isBoolean() const; + bool isNumber() const; + bool isString() const; + bool isGetterSetter() const; + bool isObject() const; + bool inherits(const ClassInfo*) const; + + // Extracting the value. + bool getBoolean(bool&) const; + bool getBoolean() const; // false if not a boolean + bool getNumber(double&) const; + double uncheckedGetNumber() const; + bool getString(ExecState* exec, UString&) const; + UString getString(ExecState* exec) const; // null string if not a string + JSObject* getObject() const; // 0 if not an object + + // Extracting integer values. + bool getUInt32(uint32_t&) const; + + // Basic conversions. + JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&); + + bool toBoolean(ExecState*) const; + + // toNumber conversion is expected to be side effect free if an exception has + // been set in the ExecState already. + double toNumber(ExecState*) const; + JSValue toJSNumber(ExecState*) const; // Fast path for when you expect that the value is an immediate number. + UString toString(ExecState*) const; + UString toPrimitiveString(ExecState*) const; + JSObject* toObject(ExecState*) const; + + // Integer conversions. + double toInteger(ExecState*) const; + double toIntegerPreserveNaN(ExecState*) const; + int32_t toInt32(ExecState*) const; + uint32_t toUInt32(ExecState*) const; + +#if ENABLE(JSC_ZOMBIES) + bool isZombie() const; +#endif + + // Floating point conversions (this is a convenience method for webcore; + // signle precision float is not a representation used in JS or JSC). + float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); } + + // Object operations, with the toObject operation included. + JSValue get(ExecState*, const Identifier& propertyName) const; + JSValue get(ExecState*, const Identifier& propertyName, PropertySlot&) const; + JSValue get(ExecState*, unsigned propertyName) const; + JSValue get(ExecState*, unsigned propertyName, PropertySlot&) const; + void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + void putDirect(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + void put(ExecState*, unsigned propertyName, JSValue); + + bool needsThisConversion() const; + JSObject* toThisObject(ExecState*) const; + JSValue toStrictThisObject(ExecState*) const; + UString toThisString(ExecState*) const; + JSString* toThisJSString(ExecState*) const; + + static bool equal(ExecState* exec, JSValue v1, JSValue v2); + static bool equalSlowCase(ExecState* exec, JSValue v1, JSValue v2); + static bool equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqual(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2); + + JSValue getJSNumber(); // JSValue() if this is not a JSNumber or number object + + bool isCell() const; + JSCell* asCell() const; + bool isValidCallee(); + +#ifndef NDEBUG + char* description(); +#endif + + private: + enum HashTableDeletedValueTag { HashTableDeletedValue }; + JSValue(HashTableDeletedValueTag); + + inline const JSValue asValue() const { return *this; } + JSObject* toObjectSlowCase(ExecState*) const; + JSObject* toThisObjectSlowCase(ExecState*) const; + + JSObject* synthesizePrototype(ExecState*) const; + JSObject* synthesizeObject(ExecState*) const; + +#if USE(JSVALUE32_64) + enum { NullTag = 0xffffffff }; + enum { UndefinedTag = 0xfffffffe }; + enum { Int32Tag = 0xfffffffd }; + enum { CellTag = 0xfffffffc }; + enum { TrueTag = 0xfffffffb }; + enum { FalseTag = 0xfffffffa }; + enum { EmptyValueTag = 0xfffffff9 }; + enum { DeletedValueTag = 0xfffffff8 }; + + enum { LowestTag = DeletedValueTag }; + + uint32_t tag() const; + int32_t payload() const; + + union { + EncodedJSValue asEncodedJSValue; + double asDouble; +#if CPU(BIG_ENDIAN) + struct { + int32_t tag; + int32_t payload; + } asBits; +#else + struct { + int32_t payload; + int32_t tag; + } asBits; +#endif + } u; +#else // USE(JSVALUE32_64) + JSCell* m_ptr; +#endif // USE(JSVALUE32_64) + }; + +#if USE(JSVALUE32_64) + typedef IntHash<EncodedJSValue> EncodedJSValueHash; + + struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> { + static const bool emptyValueIsZero = false; + static EncodedJSValue emptyValue() { return JSValue::encode(JSValue()); } + static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + }; +#else + typedef PtrHash<EncodedJSValue> EncodedJSValueHash; + + struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> { + static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + }; +#endif + + // Stand-alone helper functions. + inline JSValue jsNull() + { + return JSValue(JSValue::JSNull); + } + + inline JSValue jsUndefined() + { + return JSValue(JSValue::JSUndefined); + } + + inline JSValue jsBoolean(bool b) + { + return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse); + } + + ALWAYS_INLINE JSValue jsDoubleNumber(double d) + { + return JSValue(JSValue::EncodeAsDouble, d); + } + + ALWAYS_INLINE JSValue jsNumber(double d) + { + return JSValue(d); + } + + ALWAYS_INLINE JSValue jsNumber(char i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(unsigned char i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(short i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(unsigned short i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(int i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(unsigned i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(long i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(unsigned long i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(long long i) + { + return JSValue(i); + } + + ALWAYS_INLINE JSValue jsNumber(unsigned long long i) + { + return JSValue(i); + } + + inline bool operator==(const JSValue a, const JSCell* b) { return a == JSValue(b); } + inline bool operator==(const JSCell* a, const JSValue b) { return JSValue(a) == b; } + + inline bool operator!=(const JSValue a, const JSCell* b) { return a != JSValue(b); } + inline bool operator!=(const JSCell* a, const JSValue b) { return JSValue(a) != b; } + + ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const + { + if (isInt32()) + return asInt32(); + return JSC::toInt32(toNumber(exec)); + } + + inline uint32_t JSValue::toUInt32(ExecState* exec) const + { + // See comment on JSC::toUInt32, above. + return toInt32(exec); + } + +#if USE(JSVALUE32_64) + inline JSValue jsNaN() + { + return JSValue(nonInlineNaN()); + } + + // JSValue member functions. + inline EncodedJSValue JSValue::encode(JSValue value) + { + return value.u.asEncodedJSValue; + } + + inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) + { + JSValue v; + v.u.asEncodedJSValue = encodedJSValue; +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif + return v; + } + + inline JSValue::JSValue() + { + u.asBits.tag = EmptyValueTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSNullTag) + { + u.asBits.tag = NullTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSUndefinedTag) + { + u.asBits.tag = UndefinedTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSTrueTag) + { + u.asBits.tag = TrueTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSFalseTag) + { + u.asBits.tag = FalseTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(HashTableDeletedValueTag) + { + u.asBits.tag = DeletedValueTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSCell* ptr) + { + if (ptr) + u.asBits.tag = CellTag; + else + u.asBits.tag = EmptyValueTag; + u.asBits.payload = reinterpret_cast<int32_t>(ptr); +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif + } + + inline JSValue::JSValue(const JSCell* ptr) + { + if (ptr) + u.asBits.tag = CellTag; + else + u.asBits.tag = EmptyValueTag; + u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif + } + + inline JSValue::operator bool() const + { + ASSERT(tag() != DeletedValueTag); + return tag() != EmptyValueTag; + } + + inline bool JSValue::operator==(const JSValue& other) const + { + return u.asEncodedJSValue == other.u.asEncodedJSValue; + } + + inline bool JSValue::operator!=(const JSValue& other) const + { + return u.asEncodedJSValue != other.u.asEncodedJSValue; + } + + inline bool JSValue::isUndefined() const + { + return tag() == UndefinedTag; + } + + inline bool JSValue::isNull() const + { + return tag() == NullTag; + } + + inline bool JSValue::isUndefinedOrNull() const + { + return isUndefined() || isNull(); + } + + inline bool JSValue::isCell() const + { + return tag() == CellTag; + } + + inline bool JSValue::isInt32() const + { + return tag() == Int32Tag; + } + + inline bool JSValue::isUInt32() const + { + return tag() == Int32Tag && asInt32() > -1; + } + + inline bool JSValue::isDouble() const + { + return tag() < LowestTag; + } + + inline bool JSValue::isTrue() const + { + return tag() == TrueTag; + } + + inline bool JSValue::isFalse() const + { + return tag() == FalseTag; + } + + inline uint32_t JSValue::tag() const + { + return u.asBits.tag; + } + + inline int32_t JSValue::payload() const + { + return u.asBits.payload; + } + + inline int32_t JSValue::asInt32() const + { + ASSERT(isInt32()); + return u.asBits.payload; + } + + inline uint32_t JSValue::asUInt32() const + { + ASSERT(isUInt32()); + return u.asBits.payload; + } + + inline double JSValue::asDouble() const + { + ASSERT(isDouble()); + return u.asDouble; + } + + ALWAYS_INLINE JSCell* JSValue::asCell() const + { + ASSERT(isCell()); + return reinterpret_cast<JSCell*>(u.asBits.payload); + } + + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) + { + u.asDouble = d; + } + + inline JSValue::JSValue(double d) + { + const int32_t asInt32 = static_cast<int32_t>(d); + if (asInt32 != d || (!asInt32 && signbit(d))) { // true for -0.0 + u.asDouble = d; + return; + } + *this = JSValue(static_cast<int32_t>(d)); + } + + inline JSValue::JSValue(char i) + { + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(unsigned char i) + { + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(short i) + { + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(unsigned short i) + { + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(int i) + { + u.asBits.tag = Int32Tag; + u.asBits.payload = i; + } + + inline JSValue::JSValue(unsigned i) + { + if (static_cast<int32_t>(i) < 0) { + *this = JSValue(static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(long i) + { + if (static_cast<int32_t>(i) != i) { + *this = JSValue(static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(unsigned long i) + { + if (static_cast<uint32_t>(i) != i) { + *this = JSValue(static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<uint32_t>(i)); + } + + inline JSValue::JSValue(long long i) + { + if (static_cast<int32_t>(i) != i) { + *this = JSValue(static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(unsigned long long i) + { + if (static_cast<uint32_t>(i) != i) { + *this = JSValue(static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<uint32_t>(i)); + } + + inline bool JSValue::isNumber() const + { + return isInt32() || isDouble(); + } + + inline bool JSValue::isBoolean() const + { + return isTrue() || isFalse(); + } + + inline bool JSValue::getBoolean(bool& v) const + { + if (isTrue()) { + v = true; + return true; + } + if (isFalse()) { + v = false; + return true; + } + + return false; + } + + inline bool JSValue::getBoolean() const + { + ASSERT(isBoolean()); + return tag() == TrueTag; + } + + inline double JSValue::uncheckedGetNumber() const + { + ASSERT(isNumber()); + return isInt32() ? asInt32() : asDouble(); + } + + ALWAYS_INLINE JSValue JSValue::toJSNumber(ExecState* exec) const + { + return isNumber() ? asValue() : jsNumber(this->toNumber(exec)); + } + + inline bool JSValue::getNumber(double& result) const + { + if (isInt32()) { + result = asInt32(); + return true; + } + if (isDouble()) { + result = asDouble(); + return true; + } + return false; + } + +#else // USE(JSVALUE32_64) + + // JSValue member functions. + inline EncodedJSValue JSValue::encode(JSValue value) + { + return reinterpret_cast<EncodedJSValue>(value.m_ptr); + } + + inline JSValue JSValue::decode(EncodedJSValue ptr) + { + return JSValue(reinterpret_cast<JSCell*>(ptr)); + } + + inline JSValue JSValue::makeImmediate(intptr_t value) + { + return JSValue(reinterpret_cast<JSCell*>(value)); + } + + inline intptr_t JSValue::immediateValue() + { + return reinterpret_cast<intptr_t>(m_ptr); + } + + // 0x0 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x0, which is in the (invalid) zero page. + inline JSValue::JSValue() + : m_ptr(0) + { + } + + // 0x4 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x4, which is in the (invalid) zero page. + inline JSValue::JSValue(HashTableDeletedValueTag) + : m_ptr(reinterpret_cast<JSCell*>(0x4)) + { + } + + inline JSValue::JSValue(JSCell* ptr) + : m_ptr(ptr) + { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif + } + + inline JSValue::JSValue(const JSCell* ptr) + : m_ptr(const_cast<JSCell*>(ptr)) + { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif + } + + inline JSValue::operator bool() const + { + return m_ptr; + } + + inline bool JSValue::operator==(const JSValue& other) const + { + return m_ptr == other.m_ptr; + } + + inline bool JSValue::operator!=(const JSValue& other) const + { + return m_ptr != other.m_ptr; + } + + inline bool JSValue::isUndefined() const + { + return asValue() == jsUndefined(); + } + + inline bool JSValue::isNull() const + { + return asValue() == jsNull(); + } +#endif // USE(JSVALUE32_64) + + typedef std::pair<JSValue, UString> ValueStringPair; +} // namespace JSC + +#endif // JSValue_h diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.cpp b/Source/JavaScriptCore/runtime/JSVariableObject.cpp new file mode 100644 index 0000000..81d05ba --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSVariableObject.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007, 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JSVariableObject.h" + +#include "PropertyNameArray.h" +#include "PropertyDescriptor.h" + +namespace JSC { + +bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (symbolTable().contains(propertyName.impl())) + return false; + + return JSObject::deleteProperty(exec, propertyName); +} + +void JSVariableObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + SymbolTable::const_iterator end = symbolTable().end(); + for (SymbolTable::const_iterator it = symbolTable().begin(); it != end; ++it) { + if (!(it->second.getAttributes() & DontEnum) || (mode == IncludeDontEnumProperties)) + propertyNames.add(Identifier(exec, it->first.get())); + } + + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +bool JSVariableObject::isVariableObject() const +{ + return true; +} + +bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + descriptor.setDescriptor(registerAt(entry.getIndex()).jsValue(), entry.getAttributes() | DontDelete); + return true; + } + return false; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.h b/Source/JavaScriptCore/runtime/JSVariableObject.h new file mode 100644 index 0000000..3f2e218 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSVariableObject.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2007, 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 JSVariableObject_h +#define JSVariableObject_h + +#include "JSObject.h" +#include "Register.h" +#include "SymbolTable.h" +#include "UnusedParam.h" +#include <wtf/OwnArrayPtr.h> +#include <wtf/UnusedParam.h> + +namespace JSC { + + class Register; + + class JSVariableObject : public JSObject { + friend class JIT; + + public: + SymbolTable& symbolTable() const { return *d->symbolTable; } + + virtual void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes) = 0; + + virtual bool deleteProperty(ExecState*, const Identifier&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + virtual bool isVariableObject() const; + virtual bool isDynamicScope(bool& requiresDynamicChecks) const = 0; + + Register& registerAt(int index) const { return d->registers[index]; } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesGetPropertyNames | JSObject::StructureFlags; + // Subclasses of JSVariableObject can subclass this struct to add data + // without increasing their own size (since there's a hard limit on the + // size of a JSCell). + struct JSVariableObjectData { + JSVariableObjectData(SymbolTable* symbolTable, Register* registers) + : symbolTable(symbolTable) + , registers(registers) + { + ASSERT(symbolTable); + } + + SymbolTable* symbolTable; // Maps name -> offset from "r" in register file. + Register* registers; // "r" in the register file. + OwnArrayPtr<Register> registerArray; // Independent copy of registers, used when a variable object copies its registers out of the register file. + + private: + JSVariableObjectData(const JSVariableObjectData&); + JSVariableObjectData& operator=(const JSVariableObjectData&); + }; + + JSVariableObject(NonNullPassRefPtr<Structure> structure, JSVariableObjectData* data) + : JSObject(structure) + , d(data) // Subclass owns this pointer. + { + } + + Register* copyRegisterArray(Register* src, size_t count); + 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); + + JSVariableObjectData* d; + }; + + inline bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) + { + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + slot.setRegisterSlot(®isterAt(entry.getIndex())); + return true; + } + return false; + } + + inline bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable) + { + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + slot.setRegisterSlot(®isterAt(entry.getIndex())); + slotIsWriteable = !entry.isReadOnly(); + return true; + } + return false; + } + + inline bool JSVariableObject::symbolTablePut(const Identifier& propertyName, JSValue value) + { + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (entry.isNull()) + return false; + if (entry.isReadOnly()) + return true; + registerAt(entry.getIndex()) = value; + return true; + } + + inline bool JSVariableObject::symbolTablePutWithAttributes(const Identifier& propertyName, JSValue value, unsigned attributes) + { + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTable::iterator iter = symbolTable().find(propertyName.impl()); + if (iter == symbolTable().end()) + return false; + SymbolTableEntry& entry = iter->second; + ASSERT(!entry.isNull()); + entry.setAttributes(attributes); + registerAt(entry.getIndex()) = value; + return true; + } + + inline Register* JSVariableObject::copyRegisterArray(Register* src, size_t count) + { + Register* registerArray = new Register[count]; + memcpy(registerArray, src, count * sizeof(Register)); + + return registerArray; + } + + inline void JSVariableObject::setRegisters(Register* registers, Register* registerArray) + { + ASSERT(registerArray != d->registerArray.get()); + d->registerArray.set(registerArray); + d->registers = registers; + } + +} // namespace JSC + +#endif // JSVariableObject_h diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.cpp b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp new file mode 100644 index 0000000..2c39f5c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2006 Maks Orlovich + * Copyright (C) 2006, 2009 Apple, Inc. + * + * 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 "JSWrapperObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSWrapperObject); + +void JSWrapperObject::markChildren(MarkStack& markStack) +{ + JSObject::markChildren(markStack); + if (m_internalValue) + markStack.append(m_internalValue); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.h b/Source/JavaScriptCore/runtime/JSWrapperObject.h new file mode 100644 index 0000000..f19cd30 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 Maks Orlovich + * Copyright (C) 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 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. + * + */ + +#ifndef JSWrapperObject_h +#define JSWrapperObject_h + +#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(NonNullPassRefPtr<Structure>); + + public: + JSValue internalValue() const { return m_internalValue; } + void setInternalValue(JSValue); + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned AnonymousSlotCount = 1 + JSObject::AnonymousSlotCount; + + private: + virtual void markChildren(MarkStack&); + + JSValue m_internalValue; + }; + + inline JSWrapperObject::JSWrapperObject(NonNullPassRefPtr<Structure> structure) + : JSObject(structure) + { + putAnonymousValue(0, jsNull()); + } + + inline void JSWrapperObject::setInternalValue(JSValue value) + { + ASSERT(value); + ASSERT(!value.isObject()); + m_internalValue = value; + putAnonymousValue(0, value); + } + +} // namespace JSC + +#endif // JSWrapperObject_h diff --git a/Source/JavaScriptCore/runtime/JSZombie.cpp b/Source/JavaScriptCore/runtime/JSZombie.cpp new file mode 100644 index 0000000..8a36bda --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSZombie.cpp @@ -0,0 +1,48 @@ +/* + * 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 "JSZombie.h" +#include "ClassInfo.h" + +#if ENABLE(JSC_ZOMBIES) + +namespace JSC { + +const ClassInfo JSZombie::s_info = { "Zombie", 0, 0, 0 }; + +Structure* JSZombie::leakedZombieStructure() { + static Structure* structure = 0; + if (!structure) { + Structure::startIgnoringLeaks(); + structure = Structure::create(jsNull(), TypeInfo(UnspecifiedType), 0).leakRef(); + Structure::stopIgnoringLeaks(); + } + return structure; +} + +} + +#endif // ENABLE(JSC_ZOMBIES) diff --git a/Source/JavaScriptCore/runtime/JSZombie.h b/Source/JavaScriptCore/runtime/JSZombie.h new file mode 100644 index 0000000..da45699 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSZombie.h @@ -0,0 +1,77 @@ +/* + * 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 JSZombie_h +#define JSZombie_h + +#include "JSCell.h" + +#if ENABLE(JSC_ZOMBIES) +namespace JSC { + +class JSZombie : public JSCell { +public: + JSZombie(const ClassInfo* oldInfo, Structure* structure) + : JSCell(structure) + , m_oldInfo(oldInfo) + { + } + virtual bool isZombie() const { return true; } + virtual const ClassInfo* classInfo() const { return &s_info; } + static Structure* leakedZombieStructure(); + + virtual bool isGetterSetter() const { ASSERT_NOT_REACHED(); return false; } + virtual bool isAPIValueWrapper() const { ASSERT_NOT_REACHED(); return false; } + virtual bool isPropertyNameIterator() const { ASSERT_NOT_REACHED(); return false; } + virtual CallType getCallData(CallData&) { ASSERT_NOT_REACHED(); return CallTypeNone; } + virtual ConstructType getConstructData(ConstructData&) { ASSERT_NOT_REACHED(); return ConstructTypeNone; } + virtual bool getUInt32(uint32_t&) const { ASSERT_NOT_REACHED(); return false; } + virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const { ASSERT_NOT_REACHED(); return jsNull(); } + virtual bool getPrimitiveNumber(ExecState*, double&, JSValue&) { ASSERT_NOT_REACHED(); return false; } + virtual bool toBoolean(ExecState*) const { ASSERT_NOT_REACHED(); return false; } + virtual double toNumber(ExecState*) const { ASSERT_NOT_REACHED(); return 0.0; } + virtual UString toString(ExecState*) const { ASSERT_NOT_REACHED(); return ""; } + virtual JSObject* toObject(ExecState*) const { ASSERT_NOT_REACHED(); return 0; } + virtual void markChildren(MarkStack&) { ASSERT_NOT_REACHED(); } + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&) { ASSERT_NOT_REACHED(); } + virtual void put(ExecState*, unsigned, JSValue) { ASSERT_NOT_REACHED(); } + virtual bool deleteProperty(ExecState*, const Identifier&) { ASSERT_NOT_REACHED(); return false; } + virtual bool deleteProperty(ExecState*, unsigned) { ASSERT_NOT_REACHED(); return false; } + virtual JSObject* toThisObject(ExecState*) const { ASSERT_NOT_REACHED(); return 0; } + virtual JSValue toStrictThisObject(ExecState*) const { ASSERT_NOT_REACHED(); return JSValue(); } + virtual JSValue getJSNumber() { ASSERT_NOT_REACHED(); return jsNull(); } + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&) { ASSERT_NOT_REACHED(); return false; } + virtual bool getOwnPropertySlot(ExecState*, unsigned, PropertySlot&) { ASSERT_NOT_REACHED(); return false; } + + static const ClassInfo s_info; +private: + const ClassInfo* m_oldInfo; +}; + +} + +#endif // ENABLE(JSC_ZOMBIES) + +#endif // JSZombie_h diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp new file mode 100644 index 0000000..df87e7f --- /dev/null +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -0,0 +1,462 @@ +/* + * 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 "LiteralParser.h" + +#include "JSArray.h" +#include "JSString.h" +#include "Lexer.h" +#include "UStringBuilder.h" +#include <wtf/ASCIICType.h> +#include <wtf/dtoa.h> + +namespace JSC { + +static inline bool isJSONWhiteSpace(const UChar& c) +{ + // The JSON RFC 4627 defines a list of allowed characters to be considered + // insignificant white space: http://www.ietf.org/rfc/rfc4627.txt (2. JSON Grammar). + return c == ' ' || c == 0x9 || c == 0xA || c == 0xD; +} + +LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) +{ + while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr)) + ++m_ptr; + + ASSERT(m_ptr <= m_end); + if (m_ptr >= m_end) { + token.type = TokEnd; + token.start = token.end = m_ptr; + return TokEnd; + } + token.type = TokError; + token.start = m_ptr; + switch (*m_ptr) { + case '[': + token.type = TokLBracket; + token.end = ++m_ptr; + return TokLBracket; + case ']': + token.type = TokRBracket; + token.end = ++m_ptr; + return TokRBracket; + case '(': + token.type = TokLParen; + token.end = ++m_ptr; + return TokLBracket; + case ')': + token.type = TokRParen; + token.end = ++m_ptr; + return TokRBracket; + case '{': + token.type = TokLBrace; + token.end = ++m_ptr; + return TokLBrace; + case '}': + token.type = TokRBrace; + token.end = ++m_ptr; + return TokRBrace; + case ',': + token.type = TokComma; + token.end = ++m_ptr; + return TokComma; + case ':': + token.type = TokColon; + token.end = ++m_ptr; + return TokColon; + case '"': + if (m_mode == StrictJSON) + return lexString<StrictJSON>(token); + return lexString<NonStrictJSON>(token); + case 't': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') { + m_ptr += 4; + token.type = TokTrue; + token.end = m_ptr; + return TokTrue; + } + break; + case 'f': + if (m_end - m_ptr >= 5 && m_ptr[1] == 'a' && m_ptr[2] == 'l' && m_ptr[3] == 's' && m_ptr[4] == 'e') { + m_ptr += 5; + token.type = TokFalse; + token.end = m_ptr; + return TokFalse; + } + break; + case 'n': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'u' && m_ptr[2] == 'l' && m_ptr[3] == 'l') { + m_ptr += 4; + token.type = TokNull; + token.end = m_ptr; + return TokNull; + } + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return lexNumber(token); + } + return TokError; +} + +template <LiteralParser::ParserMode mode> static inline bool isSafeStringCharacter(UChar c) +{ + return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != '"') || c == '\t'; +} + +// "inline" is required here to help WINSCW compiler resolve specialized argument in templated functions. +template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token) +{ + ++m_ptr; + const UChar* runStart; + UStringBuilder builder; + do { + runStart = m_ptr; + while (m_ptr < m_end && isSafeStringCharacter<mode>(*m_ptr)) + ++m_ptr; + if (runStart < m_ptr) + builder.append(runStart, m_ptr - runStart); + if ((mode == StrictJSON) && m_ptr < m_end && *m_ptr == '\\') { + ++m_ptr; + if (m_ptr >= m_end) + return TokError; + switch (*m_ptr) { + case '"': + builder.append('"'); + m_ptr++; + break; + case '\\': + builder.append('\\'); + m_ptr++; + break; + case '/': + builder.append('/'); + m_ptr++; + break; + case 'b': + builder.append('\b'); + m_ptr++; + break; + case 'f': + builder.append('\f'); + m_ptr++; + break; + case 'n': + builder.append('\n'); + m_ptr++; + break; + case 'r': + builder.append('\r'); + m_ptr++; + break; + case 't': + builder.append('\t'); + m_ptr++; + break; + + case 'u': + if ((m_end - m_ptr) < 5) // uNNNN == 5 characters + return TokError; + for (int i = 1; i < 5; i++) { + if (!isASCIIHexDigit(m_ptr[i])) + return TokError; + } + builder.append(JSC::Lexer::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4])); + m_ptr += 5; + break; + + default: + return TokError; + } + } + } while ((mode == StrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != '"'); + + if (m_ptr >= m_end || *m_ptr != '"') + return TokError; + + token.stringToken = builder.toUString(); + token.type = TokString; + token.end = ++m_ptr; + return TokString; +} + +LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& token) +{ + // ES5 and json.org define numbers as + // number + // int + // int frac? exp? + // + // int + // -? 0 + // -? digit1-9 digits? + // + // digits + // digit digits? + // + // -?(0 | [1-9][0-9]*) ('.' [0-9]+)? ([eE][+-]? [0-9]+)? + + if (m_ptr < m_end && *m_ptr == '-') // -? + ++m_ptr; + + // (0 | [1-9][0-9]*) + if (m_ptr < m_end && *m_ptr == '0') // 0 + ++m_ptr; + else if (m_ptr < m_end && *m_ptr >= '1' && *m_ptr <= '9') { // [1-9] + ++m_ptr; + // [0-9]* + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } else + return TokError; + + // ('.' [0-9]+)? + if (m_ptr < m_end && *m_ptr == '.') { + ++m_ptr; + // [0-9]+ + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) + return TokError; + + ++m_ptr; + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } + + // ([eE][+-]? [0-9]+)? + if (m_ptr < m_end && (*m_ptr == 'e' || *m_ptr == 'E')) { // [eE] + ++m_ptr; + + // [-+]? + if (m_ptr < m_end && (*m_ptr == '-' || *m_ptr == '+')) + ++m_ptr; + + // [0-9]+ + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) + return TokError; + + ++m_ptr; + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } + + token.type = TokNumber; + token.end = m_ptr; + Vector<char, 64> buffer(token.end - token.start + 1); + int i; + for (i = 0; i < token.end - token.start; i++) { + ASSERT(static_cast<char>(token.start[i]) == token.start[i]); + buffer[i] = static_cast<char>(token.start[i]); + } + buffer[i] = 0; + char* end; + token.numberToken = WTF::strtod(buffer.data(), &end); + ASSERT(buffer.data() + (token.end - token.start) == end); + return TokNumber; +} + +JSValue LiteralParser::parse(ParserState initialState) +{ + ParserState state = initialState; + MarkedArgumentBuffer objectStack; + JSValue lastValue; + Vector<ParserState, 16> stateStack; + Vector<Identifier, 16> identifierStack; + while (1) { + switch(state) { + startParseArray: + case StartParseArray: { + JSArray* array = constructEmptyArray(m_exec); + objectStack.append(array); + // fallthrough + } + 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(); + break; + } + + stateStack.append(DoParseArrayEndExpression); + goto startParseExpression; + } + case DoParseArrayEndExpression: { + asArray(objectStack.last())->push(m_exec, lastValue); + + if (m_lexer.currentToken().type == TokComma) + goto doParseArrayStartExpression; + + if (m_lexer.currentToken().type != TokRBracket) + return JSValue(); + + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + startParseObject: + case StartParseObject: { + JSObject* object = constructEmptyObject(m_exec); + objectStack.append(object); + + TokenType type = m_lexer.next(); + if (type == TokString) { + Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) + return JSValue(); + + m_lexer.next(); + identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } else if (type != TokRBrace) + return JSValue(); + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + doParseObjectStartExpression: + case DoParseObjectStartExpression: { + TokenType type = m_lexer.next(); + if (type != TokString) + return JSValue(); + Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) + return JSValue(); + + m_lexer.next(); + identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } + case DoParseObjectEndExpression: + { + asObject(objectStack.last())->putDirect(identifierStack.last(), lastValue); + identifierStack.removeLast(); + if (m_lexer.currentToken().type == TokComma) + goto doParseObjectStartExpression; + if (m_lexer.currentToken().type != TokRBrace) + return JSValue(); + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + startParseExpression: + case StartParseExpression: { + switch (m_lexer.currentToken().type) { + case TokLBracket: + goto startParseArray; + case TokLBrace: + goto startParseObject; + case TokString: { + Lexer::LiteralParserToken stringToken = m_lexer.currentToken(); + m_lexer.next(); + lastValue = jsString(m_exec, stringToken.stringToken); + break; + } + case TokNumber: { + Lexer::LiteralParserToken numberToken = m_lexer.currentToken(); + m_lexer.next(); + lastValue = jsNumber(numberToken.numberToken); + break; + } + case TokNull: + m_lexer.next(); + lastValue = jsNull(); + break; + + case TokTrue: + m_lexer.next(); + lastValue = jsBoolean(true); + break; + + case TokFalse: + m_lexer.next(); + lastValue = jsBoolean(false); + break; + + default: + // Error + return JSValue(); + } + break; + } + case StartParseStatement: { + switch (m_lexer.currentToken().type) { + case TokLBracket: + case TokNumber: + case TokString: + goto startParseExpression; + + case TokLParen: { + m_lexer.next(); + stateStack.append(StartParseStatementEndStatement); + goto startParseExpression; + } + default: + return JSValue(); + } + } + case StartParseStatementEndStatement: { + ASSERT(stateStack.isEmpty()); + if (m_lexer.currentToken().type != TokRParen) + return JSValue(); + if (m_lexer.next() == TokEnd) + return lastValue; + return JSValue(); + } + default: + ASSERT_NOT_REACHED(); + } + if (stateStack.isEmpty()) + return lastValue; + state = stateStack.last(); + stateStack.removeLast(); + continue; + } +} + +} diff --git a/Source/JavaScriptCore/runtime/LiteralParser.h b/Source/JavaScriptCore/runtime/LiteralParser.h new file mode 100644 index 0000000..6df5d06 --- /dev/null +++ b/Source/JavaScriptCore/runtime/LiteralParser.h @@ -0,0 +1,110 @@ +/* + * 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 LiteralParser_h +#define LiteralParser_h + +#include "JSGlobalObjectFunctions.h" +#include "JSValue.h" +#include "UString.h" + +namespace JSC { + + class LiteralParser { + public: + typedef enum { StrictJSON, NonStrictJSON } ParserMode; + LiteralParser(ExecState* exec, const UString& s, ParserMode mode) + : m_exec(exec) + , m_lexer(s, mode) + , m_mode(mode) + { + } + + JSValue tryLiteralParse() + { + m_lexer.next(); + JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement); + if (m_lexer.currentToken().type != TokEnd) + return JSValue(); + return result; + } + private: + enum ParserState { StartParseObject, StartParseArray, StartParseExpression, + StartParseStatement, StartParseStatementEndStatement, + DoParseObjectStartExpression, DoParseObjectEndExpression, + DoParseArrayStartExpression, DoParseArrayEndExpression }; + enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace, + TokString, TokIdentifier, TokNumber, TokColon, + TokLParen, TokRParen, TokComma, TokTrue, TokFalse, + TokNull, TokEnd, TokError }; + + class Lexer { + public: + struct LiteralParserToken { + TokenType type; + const UChar* start; + const UChar* end; + UString stringToken; + double numberToken; + }; + Lexer(const UString& s, ParserMode mode) + : m_string(s) + , m_mode(mode) + , m_ptr(s.characters()) + , m_end(s.characters() + s.length()) + { + } + + TokenType next() + { + return lex(m_currentToken); + } + + const LiteralParserToken& currentToken() + { + return m_currentToken; + } + + private: + TokenType lex(LiteralParserToken&); + template <ParserMode mode> TokenType lexString(LiteralParserToken&); + TokenType lexNumber(LiteralParserToken&); + LiteralParserToken m_currentToken; + UString m_string; + ParserMode m_mode; + const UChar* m_ptr; + const UChar* m_end; + }; + + class StackGuard; + JSValue parse(ParserState); + + ExecState* m_exec; + LiteralParser::Lexer m_lexer; + ParserMode m_mode; + }; +} + +#endif diff --git a/Source/JavaScriptCore/runtime/Lookup.cpp b/Source/JavaScriptCore/runtime/Lookup.cpp new file mode 100644 index 0000000..dac1c94 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Lookup.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "Lookup.h" + +#include "Executable.h" +#include "JSFunction.h" +#include "PrototypeFunction.h" + +namespace JSC { + +void HashTable::createTable(JSGlobalData* globalData) const +{ + ASSERT(!table); + int linkIndex = compactHashSizeMask + 1; + HashEntry* entries = new HashEntry[compactSize]; + for (int i = 0; i < compactSize; ++i) + entries[i].setKey(0); + for (int i = 0; values[i].key; ++i) { + StringImpl* identifier = Identifier::add(globalData, values[i].key).leakRef(); + int hashIndex = identifier->existingHash() & compactHashSizeMask; + HashEntry* entry = &entries[hashIndex]; + + if (entry->key()) { + while (entry->next()) { + entry = entry->next(); + } + ASSERT(linkIndex < compactSize); + entry->setNext(&entries[linkIndex++]); + entry = entry->next(); + } + + entry->initialize(identifier, values[i].attributes, values[i].value1, values[i].value2 +#if ENABLE(JIT) + , values[i].generator +#endif + ); + } + table = entries; +} + +void HashTable::deleteTable() const +{ + if (table) { + int max = compactSize; + for (int i = 0; i != max; ++i) { + if (StringImpl* key = table[i].key()) + key->deref(); + } + delete [] table; + table = 0; + } +} + +void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) +{ + ASSERT(thisObj->structure()->anonymousSlotCount() > 0); + ASSERT(thisObj->getAnonymousValue(0).isCell() && asObject(thisObj->getAnonymousValue(0).asCell())->isGlobalObject()); + ASSERT(entry->attributes() & Function); + JSValue* location = thisObj->getDirectLocation(propertyName); + + if (!location) { + NativeFunctionWrapper* function; + JSGlobalObject* globalObject = asGlobalObject(thisObj->getAnonymousValue(0).asCell()); +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + if (entry->generator()) + function = new (exec) NativeFunctionWrapper(exec, globalObject, globalObject->prototypeFunctionStructure(), entry->functionLength(), propertyName, exec->globalData().getHostFunction(entry->function(), entry->generator())); + else +#endif + function = new (exec) NativeFunctionWrapper(exec, globalObject, globalObject->prototypeFunctionStructure(), entry->functionLength(), propertyName, entry->function()); + + thisObj->putDirectFunction(propertyName, function, entry->attributes()); + location = thisObj->getDirectLocation(propertyName); + } + + slot.setValueSlot(thisObj, location, thisObj->offsetForLocation(location)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h new file mode 100644 index 0000000..9bc81d4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Lookup.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef Lookup_h +#define Lookup_h + +#include "CallFrame.h" +#include "Identifier.h" +#include "JSGlobalObject.h" +#include "JSObject.h" +#include "PropertySlot.h" +#include <stdio.h> +#include <wtf/Assertions.h> + +// Bug #26843: Work around Metrowerks compiler bug +#if COMPILER(WINSCW) +#define JSC_CONST_HASHTABLE +#else +#define JSC_CONST_HASHTABLE const +#endif + +namespace JSC { + // Hash table generated by the create_hash_table script. + struct HashTableValue { + const char* key; // property name + unsigned char attributes; // JSObject attributes + intptr_t value1; + intptr_t value2; +#if ENABLE(JIT) + ThunkGenerator generator; +#endif + }; + + // FIXME: There is no reason this get function can't be simpler. + // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject) + typedef PropertySlot::GetValueFunc GetFunction; + typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value); + + class HashEntry : public FastAllocBase { + public: + void initialize(StringImpl* key, unsigned char attributes, intptr_t v1, intptr_t v2 +#if ENABLE(JIT) + , ThunkGenerator generator = 0 +#endif + ) + { + m_key = key; + m_attributes = attributes; + m_u.store.value1 = v1; + m_u.store.value2 = v2; +#if ENABLE(JIT) + m_u.function.generator = generator; +#endif + m_next = 0; + } + + void setKey(StringImpl* key) { m_key = key; } + StringImpl* key() const { return m_key; } + + unsigned char attributes() const { return m_attributes; } + +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + ThunkGenerator generator() const { ASSERT(m_attributes & Function); return m_u.function.generator; } +#endif + NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; } + unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); } + + GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; } + PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; } + + intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; } + + void setNext(HashEntry *next) { m_next = next; } + HashEntry* next() const { return m_next; } + + private: + StringImpl* m_key; + unsigned char m_attributes; // JSObject attributes + + union { + struct { + intptr_t value1; + intptr_t value2; + } store; + struct { + NativeFunction functionValue; + intptr_t length; // number of arguments for function +#if ENABLE(JIT) + ThunkGenerator generator; +#endif + } function; + struct { + GetFunction get; + PutFunction put; + } property; + struct { + intptr_t value; + intptr_t unused; + } lexer; + } m_u; + + HashEntry* m_next; + }; + + struct HashTable { + + int compactSize; + int compactHashSizeMask; + + const HashTableValue* values; // Fixed values generated by script. + mutable const HashEntry* table; // Table allocated at runtime. + + ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const + { + if (!table) + createTable(globalData); + } + + ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const + { + if (!table) + createTable(&exec->globalData()); + } + + void deleteTable() const; + + // Find an entry in the table, and return the entry. + ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const + { + initializeIfNeeded(globalData); + return entry(identifier); + } + + ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const + { + initializeIfNeeded(exec); + return entry(identifier); + } + + private: + ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const + { + ASSERT(table); + + const HashEntry* entry = &table[identifier.impl()->existingHash() & compactHashSizeMask]; + + if (!entry->key()) + return 0; + + do { + if (entry->key() == identifier.impl()) + return entry; + entry = entry->next(); + } while (entry); + + return 0; + } + + // Convert the hash table keys to identifiers. + void createTable(JSGlobalData*) const; + }; + + void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&); + + /** + * This method does it all (looking in the hashtable, checking for function + * overrides, creating the function or retrieving from cache, calling + * getValueProperty in case of a non-function property, forwarding to parent if + * unknown property). + */ + template <class ThisImp, class ParentImp> + inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + if (entry->attributes() & Function) + setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + else + slot.setCacheableCustom(thisObj, entry->propertyGetter()); + + 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 + * a dummy getValueProperty. + */ + template <class ParentImp> + inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot)) + return true; + + const HashEntry* entry = table->entry(exec, propertyName); + if (!entry) + return false; + + 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". + * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. + */ + template <class ThisImp, class ParentImp> + inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + ASSERT(!(entry->attributes() & Function)); + + slot.setCacheableCustom(thisObj, entry->propertyGetter()); + return true; + } + + /** + * 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. + */ + template <class ThisImp> + inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) + return false; + + if (entry->attributes() & Function) { // function: put as override property + if (LIKELY(value.isCell())) + thisObj->putDirectFunction(propertyName, value.asCell()); + else + thisObj->putDirect(propertyName, value); + } else if (!(entry->attributes() & ReadOnly)) + entry->propertyPutter()(exec, thisObj, value); + + return true; + } + + /** + * This one is for "put". + * It calls lookupPut<ThisImp>() to set the value. If that call + * returns false (meaning no entry in the hash table was found), + * then it calls put() on the ParentImp class. + */ + template <class ThisImp, class ParentImp> + inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot) + { + if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj)) + thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent + } + +} // namespace JSC + +#endif // Lookup_h diff --git a/Source/JavaScriptCore/runtime/MarkStack.cpp b/Source/JavaScriptCore/runtime/MarkStack.cpp new file mode 100644 index 0000000..a350c35 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MarkStack.cpp @@ -0,0 +1,40 @@ +/* + * 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 "MarkStack.h" + +namespace JSC { + +size_t MarkStack::s_pageSize = 0; + +void MarkStack::compact() +{ + ASSERT(s_pageSize); + m_values.shrinkAllocation(s_pageSize); + m_markSets.shrinkAllocation(s_pageSize); +} + +} diff --git a/Source/JavaScriptCore/runtime/MarkStack.h b/Source/JavaScriptCore/runtime/MarkStack.h new file mode 100644 index 0000000..7bccadf --- /dev/null +++ b/Source/JavaScriptCore/runtime/MarkStack.h @@ -0,0 +1,190 @@ +/* + * 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 MarkStack_h +#define MarkStack_h + +#include "JSValue.h" +#include <wtf/Noncopyable.h> +#include <wtf/OSAllocator.h> + +namespace JSC { + + class JSGlobalData; + class Register; + + enum MarkSetProperties { MayContainNullValues, NoNullValues }; + + class MarkStack : Noncopyable { + public: + MarkStack(void* jsArrayVPtr) + : m_jsArrayVPtr(jsArrayVPtr) +#if !ASSERT_DISABLED + , m_isCheckingForDefaultMarkViolation(false) + , m_isDraining(false) +#endif + { + } + + ALWAYS_INLINE void append(JSValue); + void append(JSCell*); + + ALWAYS_INLINE void appendValues(Register* values, size_t count, MarkSetProperties properties = NoNullValues) + { + appendValues(reinterpret_cast<JSValue*>(values), count, properties); + } + + ALWAYS_INLINE void appendValues(JSValue* values, size_t count, MarkSetProperties properties = NoNullValues) + { + if (count) + m_markSets.append(MarkSet(values, values + count, properties)); + } + + inline void drain(); + void compact(); + + ~MarkStack() + { + ASSERT(m_markSets.isEmpty()); + ASSERT(m_values.isEmpty()); + } + + 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; + MarkSetProperties m_properties; + }; + + static void* allocateStack(size_t size) { return OSAllocator::reserveAndCommit(size); } + static void releaseStack(void* addr, size_t size) { OSAllocator::decommitAndRelease(addr, size); } + + static void initializePagesize(); + static size_t pageSize() + { + if (!s_pageSize) + initializePagesize(); + return s_pageSize; + } + + template <typename T> struct MarkStackArray { + MarkStackArray() + : m_top(0) + , m_allocated(MarkStack::pageSize()) + , m_capacity(m_allocated / sizeof(T)) + { + m_data = reinterpret_cast<T*>(allocateStack(m_allocated)); + } + + ~MarkStackArray() + { + releaseStack(m_data, m_allocated); + } + + void expand() + { + size_t oldAllocation = m_allocated; + m_allocated *= 2; + m_capacity = m_allocated / sizeof(T); + void* newData = allocateStack(m_allocated); + memcpy(newData, m_data, oldAllocation); + releaseStack(m_data, oldAllocation); + m_data = reinterpret_cast<T*>(newData); + } + + inline void append(const T& v) + { + if (m_top == m_capacity) + expand(); + m_data[m_top++] = v; + } + + inline T removeLast() + { + ASSERT(m_top); + return m_data[--m_top]; + } + + inline T& last() + { + ASSERT(m_top); + return m_data[m_top - 1]; + } + + inline bool isEmpty() + { + return m_top == 0; + } + + inline size_t size() { return m_top; } + + inline void shrinkAllocation(size_t size) + { + ASSERT(size <= m_allocated); + ASSERT(0 == (size % MarkStack::pageSize())); + if (size == m_allocated) + return; +#if OS(WINDOWS) || OS(SYMBIAN) || PLATFORM(BREWMP) + // 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); + } + + private: + size_t m_top; + size_t m_allocated; + size_t m_capacity; + T* m_data; + }; + + void* m_jsArrayVPtr; + MarkStackArray<MarkSet> m_markSets; + MarkStackArray<JSCell*> m_values; + static size_t s_pageSize; + +#if !ASSERT_DISABLED + public: + bool m_isCheckingForDefaultMarkViolation; + bool m_isDraining; +#endif + }; +} + +#endif diff --git a/Source/JavaScriptCore/runtime/MarkStackPosix.cpp b/Source/JavaScriptCore/runtime/MarkStackPosix.cpp new file mode 100644 index 0000000..2a5b298 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MarkStackPosix.cpp @@ -0,0 +1,43 @@ +/* + * 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 "MarkStack.h" + +#if OS(UNIX) && !OS(SYMBIAN) + +#include <unistd.h> +#include <sys/mman.h> + +namespace JSC { + +void MarkStack::initializePagesize() +{ + MarkStack::s_pageSize = getpagesize(); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/MarkStackSymbian.cpp b/Source/JavaScriptCore/runtime/MarkStackSymbian.cpp new file mode 100644 index 0000000..a3893d7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MarkStackSymbian.cpp @@ -0,0 +1,38 @@ +/* + 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" + +#if OS(SYMBIAN) + +#include <e32hal.h> + +namespace JSC { + +void MarkStack::initializePagesize() +{ + TInt page_size; + UserHal::PageSizeInBytes(page_size); + MarkStack::s_pageSize = page_size; +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/MarkStackWin.cpp b/Source/JavaScriptCore/runtime/MarkStackWin.cpp new file mode 100644 index 0000000..2d2a1b3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MarkStackWin.cpp @@ -0,0 +1,44 @@ +/* + * 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 "MarkStack.h" + +#if OS(WINDOWS) + +#include "windows.h" + +namespace JSC { + +void MarkStack::initializePagesize() +{ + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + MarkStack::s_pageSize = system_info.dwPageSize; +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/MathObject.cpp b/Source/JavaScriptCore/runtime/MathObject.cpp new file mode 100644 index 0000000..080d7d2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathObject.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "MathObject.h" + +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "Operations.h" +#include <time.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/RandomNumber.h> +#include <wtf/RandomNumberSeed.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(MathObject); + +static EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncASin(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncATan(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncCos(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncExp(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncLog(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncPow(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncRound(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncSin(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState*); + +} + +#include "MathObject.lut.h" + +namespace JSC { + +// ------------------------------ MathObject -------------------------------- + +const ClassInfo MathObject::info = { "Math", 0, 0, ExecState::mathTable }; + +/* Source for MathObject.lut.h +@begin mathTable + abs mathProtoFuncAbs DontEnum|Function 1 + acos mathProtoFuncACos DontEnum|Function 1 + asin mathProtoFuncASin DontEnum|Function 1 + atan mathProtoFuncATan DontEnum|Function 1 + atan2 mathProtoFuncATan2 DontEnum|Function 2 + ceil mathProtoFuncCeil DontEnum|Function 1 + cos mathProtoFuncCos DontEnum|Function 1 + exp mathProtoFuncExp DontEnum|Function 1 + floor mathProtoFuncFloor DontEnum|Function 1 + log mathProtoFuncLog DontEnum|Function 1 + max mathProtoFuncMax DontEnum|Function 2 + min mathProtoFuncMin DontEnum|Function 2 + pow mathProtoFuncPow DontEnum|Function 2 + random mathProtoFuncRandom DontEnum|Function 0 + round mathProtoFuncRound DontEnum|Function 1 + sin mathProtoFuncSin DontEnum|Function 1 + sqrt mathProtoFuncSqrt DontEnum|Function 1 + tan mathProtoFuncTan DontEnum|Function 1 +@end +*/ + +MathObject::MathObject(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSObjectWithGlobalObject(globalObject, structure) +{ + putDirectWithoutTransition(Identifier(exec, "E"), jsNumber(exp(1.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LN2"), jsNumber(log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LN10"), jsNumber(log(10.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LOG2E"), jsNumber(1.0 / log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LOG10E"), jsNumber(1.0 / log(10.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "PI"), jsNumber(piDouble), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "SQRT1_2"), jsNumber(sqrt(0.5)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "SQRT2"), jsNumber(sqrt(2.0)), DontDelete | DontEnum | ReadOnly); +} + +// ECMA 15.8 + +bool MathObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<JSObject>(exec, ExecState::mathTable(exec), this, propertyName, slot); +} + +bool MathObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::mathTable(exec), this, propertyName, descriptor); +} + +// ------------------------------ Functions -------------------------------- + +EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState* exec) +{ + return JSValue::encode(jsNumber(fabs(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(acos(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncASin(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(asin(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(atan(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState* exec) +{ + double arg0 = exec->argument(0).toNumber(exec); + double arg1 = exec->argument(1).toNumber(exec); + return JSValue::encode(jsDoubleNumber(atan2(arg0, arg1))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec) +{ + return JSValue::encode(jsNumber(ceil(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncCos(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(cos(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncExp(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(exp(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec) +{ + return JSValue::encode(jsNumber(floor(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(log(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec) +{ + unsigned argsCount = exec->argumentCount(); + double result = -Inf; + for (unsigned k = 0; k < argsCount; ++k) { + double val = exec->argument(k).toNumber(exec); + if (isnan(val)) { + result = NaN; + break; + } + if (val > result || (val == 0 && result == 0 && !signbit(val))) + result = val; + } + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState* exec) +{ + unsigned argsCount = exec->argumentCount(); + double result = +Inf; + for (unsigned k = 0; k < argsCount; ++k) { + double val = exec->argument(k).toNumber(exec); + if (isnan(val)) { + result = NaN; + break; + } + if (val < result || (val == 0 && result == 0 && signbit(val))) + result = val; + } + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncPow(ExecState* exec) +{ + // ECMA 15.8.2.1.13 + + double arg = exec->argument(0).toNumber(exec); + double arg2 = exec->argument(1).toNumber(exec); + + if (isnan(arg2)) + return JSValue::encode(jsNaN()); + if (isinf(arg2) && fabs(arg) == 1) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(pow(arg, arg2))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(exec->lexicalGlobalObject()->weakRandomNumber())); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec) +{ + double arg = exec->argument(0).toNumber(exec); + double integer = ceil(arg); + return JSValue::encode(jsNumber(integer - (integer - arg > 0.5))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncSin(ExecState* exec) +{ + return JSValue::encode(exec->globalData().cachedSin(exec->argument(0).toNumber(exec))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(sqrt(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(tan(exec->argument(0).toNumber(exec)))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/MathObject.h b/Source/JavaScriptCore/runtime/MathObject.h new file mode 100644 index 0000000..31fa2fe --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathObject.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef MathObject_h +#define MathObject_h + +#include "JSObjectWithGlobalObject.h" + +namespace JSC { + + class MathObject : public JSObjectWithGlobalObject { + public: + MathObject(ExecState*, JSGlobalObject*, 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, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObject::StructureFlags; + }; + +} // namespace JSC + +#endif // MathObject_h diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.cpp b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp new file mode 100644 index 0000000..7fafa9c --- /dev/null +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "MemoryStatistics.h" + +#include "ExecutableAllocator.h" +#include "JSGlobalData.h" +#include "RegisterFile.h" + +namespace JSC { + +Heap::Statistics heapStatistics(JSGlobalData* commonGlobalData) +{ + return commonGlobalData->heap.statistics(); +} + +GlobalMemoryStatistics globalMemoryStatistics() +{ + GlobalMemoryStatistics stats; + + stats.stackBytes = RegisterFile::committedByteCount(); +#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) + stats.JITBytes = ExecutableAllocator::committedByteCount(); +#else + stats.JITBytes = 0; +#endif + return stats; +} + +} + + diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.h b/Source/JavaScriptCore/runtime/MemoryStatistics.h new file mode 100644 index 0000000..1b92eb9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 MemoryStatistics_h +#define MemoryStatistics_h + +#include "Collector.h" + +class JSGlobalData; + +namespace JSC { + +struct GlobalMemoryStatistics { + size_t stackBytes; + size_t JITBytes; +}; + +Heap::Statistics heapStatistics(JSGlobalData* commonGlobalData); +GlobalMemoryStatistics globalMemoryStatistics(); + +} + +#endif // MemoryStatistics_h + diff --git a/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp new file mode 100644 index 0000000..eb508eb --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "NativeErrorConstructor.h" + +#include "ErrorInstance.h" +#include "JSFunction.h" +#include "JSString.h" +#include "NativeErrorPrototype.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NativeErrorConstructor); + +const ClassInfo NativeErrorConstructor::info = { "Function", &InternalFunction::info, 0, 0 }; + +NativeErrorConstructor::NativeErrorConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<Structure> prototypeStructure, const UString& nameAndMessage) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, nameAndMessage)) +{ + NativeErrorPrototype* prototype = new (exec) NativeErrorPrototype(exec, globalObject, prototypeStructure, nameAndMessage, this); + + putDirect(exec->propertyNames().length, jsNumber(1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 + putDirect(exec->propertyNames().prototype, prototype, DontDelete | ReadOnly | DontEnum); + m_errorStructure = ErrorInstance::createStructure(prototype); +} + +static EncodedJSValue JSC_HOST_CALL constructWithNativeErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +ConstructType NativeErrorConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithNativeErrorConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callNativeErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +CallType NativeErrorConstructor::getCallData(CallData& callData) +{ + callData.native.function = callNativeErrorConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NativeErrorConstructor.h b/Source/JavaScriptCore/runtime/NativeErrorConstructor.h new file mode 100644 index 0000000..1ff8207 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NativeErrorConstructor_h +#define NativeErrorConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class ErrorInstance; + class FunctionPrototype; + class NativeErrorPrototype; + + class NativeErrorConstructor : public InternalFunction { + public: + NativeErrorConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<Structure> prototypeStructure, const UString&); + + static const ClassInfo info; + + Structure* errorStructure() { return m_errorStructure.get(); } + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + + virtual const ClassInfo* classInfo() const { return &info; } + + RefPtr<Structure> m_errorStructure; + }; + +} // namespace JSC + +#endif // NativeErrorConstructor_h diff --git a/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp new file mode 100644 index 0000000..540220a --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "NativeErrorPrototype.h" + +#include "ErrorPrototype.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "NativeErrorConstructor.h" +#include "UString.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NativeErrorPrototype); + +NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const UString& nameAndMessage, NativeErrorConstructor* constructor) + : JSObjectWithGlobalObject(globalObject, structure) +{ + putDirect(exec->propertyNames().name, jsString(exec, nameAndMessage), 0); + putDirect(exec->propertyNames().message, jsString(exec, nameAndMessage), 0); + putDirect(exec->propertyNames().constructor, constructor, DontEnum); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NativeErrorPrototype.h b/Source/JavaScriptCore/runtime/NativeErrorPrototype.h new file mode 100644 index 0000000..30690d5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NativeErrorPrototype_h +#define NativeErrorPrototype_h + +#include "JSObjectWithGlobalObject.h" + +namespace JSC { + class NativeErrorConstructor; + + class NativeErrorPrototype : public JSObjectWithGlobalObject { + public: + NativeErrorPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, const UString&, NativeErrorConstructor*); + }; + +} // namespace JSC + +#endif // NativeErrorPrototype_h diff --git a/Source/JavaScriptCore/runtime/NativeFunctionWrapper.h b/Source/JavaScriptCore/runtime/NativeFunctionWrapper.h new file mode 100644 index 0000000..d4eeb3b --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeFunctionWrapper.h @@ -0,0 +1,39 @@ +/* + * 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 NativeFunctionWrapper_h +#define NativeFunctionWrapper_h + +namespace JSC { +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + class JSFunction; + typedef JSFunction NativeFunctionWrapper; +#else + class PrototypeFunction; + typedef PrototypeFunction NativeFunctionWrapper; +#endif +} + +#endif diff --git a/Source/JavaScriptCore/runtime/NumberConstructor.cpp b/Source/JavaScriptCore/runtime/NumberConstructor.cpp new file mode 100644 index 0000000..5369ca0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberConstructor.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "NumberConstructor.h" + +#include "Lookup.h" +#include "NumberObject.h" +#include "NumberPrototype.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NumberConstructor); + +static JSValue numberConstructorNaNValue(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorNegInfinity(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorPosInfinity(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorMaxValue(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorMinValue(ExecState*, JSValue, const Identifier&); + +} // namespace JSC + +#include "NumberConstructor.lut.h" + +namespace JSC { + +const ClassInfo NumberConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::numberTable }; + +/* Source for NumberConstructor.lut.h +@begin numberTable + NaN numberConstructorNaNValue DontEnum|DontDelete|ReadOnly + NEGATIVE_INFINITY numberConstructorNegInfinity DontEnum|DontDelete|ReadOnly + POSITIVE_INFINITY numberConstructorPosInfinity DontEnum|DontDelete|ReadOnly + MAX_VALUE numberConstructorMaxValue DontEnum|DontDelete|ReadOnly + MIN_VALUE numberConstructorMinValue DontEnum|DontDelete|ReadOnly +@end +*/ + +NumberConstructor::NumberConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, NumberPrototype* numberPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, numberPrototype->info.className)) +{ + // Number.Prototype + putDirectWithoutTransition(exec->propertyNames().prototype, numberPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool NumberConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + 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*, JSValue, const Identifier&) +{ + return jsNaN(); +} + +static JSValue numberConstructorNegInfinity(ExecState*, JSValue, const Identifier&) +{ + return jsNumber(-Inf); +} + +static JSValue numberConstructorPosInfinity(ExecState*, JSValue, const Identifier&) +{ + return jsNumber(Inf); +} + +static JSValue numberConstructorMaxValue(ExecState*, JSValue, const Identifier&) +{ + return jsNumber(1.7976931348623157E+308); +} + +static JSValue numberConstructorMinValue(ExecState*, JSValue, const Identifier&) +{ + return jsNumber(5E-324); +} + +// ECMA 15.7.1 +static EncodedJSValue JSC_HOST_CALL constructWithNumberConstructor(ExecState* exec) +{ + NumberObject* object = new (exec) NumberObject(exec->lexicalGlobalObject()->numberObjectStructure()); + double n = exec->argumentCount() ? exec->argument(0).toNumber(exec) : 0; + object->setInternalValue(jsNumber(n)); + return JSValue::encode(object); +} + +ConstructType NumberConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithNumberConstructor; + return ConstructTypeHost; +} + +// ECMA 15.7.2 +static EncodedJSValue JSC_HOST_CALL callNumberConstructor(ExecState* exec) +{ + return JSValue::encode(jsNumber(!exec->argumentCount() ? 0 : exec->argument(0).toNumber(exec))); +} + +CallType NumberConstructor::getCallData(CallData& callData) +{ + callData.native.function = callNumberConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberConstructor.h b/Source/JavaScriptCore/runtime/NumberConstructor.h new file mode 100644 index 0000000..d8a2593 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberConstructor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NumberConstructor_h +#define NumberConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class NumberPrototype; + + class NumberConstructor : public InternalFunction { + public: + NumberConstructor(ExecState*, JSGlobalObject*, 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, StructureFlags), AnonymousSlotCount); + } + + enum { NaNValue, NegInfinity, PosInfinity, MaxValue, MinValue }; + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | InternalFunction::StructureFlags; + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + + virtual const ClassInfo* classInfo() const { return &info; } + }; + +} // namespace JSC + +#endif // NumberConstructor_h diff --git a/Source/JavaScriptCore/runtime/NumberObject.cpp b/Source/JavaScriptCore/runtime/NumberObject.cpp new file mode 100644 index 0000000..1a7e44c --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberObject.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "NumberObject.h" + +#include "JSGlobalObject.h" +#include "NumberPrototype.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NumberObject); + +const ClassInfo NumberObject::info = { "Number", 0, 0, 0 }; + +NumberObject::NumberObject(NonNullPassRefPtr<Structure> structure) + : JSWrapperObject(structure) +{ +} + +JSValue NumberObject::getJSNumber() +{ + return internalValue(); +} + +NumberObject* constructNumber(ExecState* exec, JSValue number) +{ + NumberObject* object = new (exec) NumberObject(exec->lexicalGlobalObject()->numberObjectStructure()); + object->setInternalValue(number); + return object; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberObject.h b/Source/JavaScriptCore/runtime/NumberObject.h new file mode 100644 index 0000000..e82b593 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberObject.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NumberObject_h +#define NumberObject_h + +#include "JSWrapperObject.h" + +namespace JSC { + + class NumberObject : public JSWrapperObject { + public: + explicit NumberObject(NonNullPassRefPtr<Structure>); + + static const ClassInfo info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = JSWrapperObject::StructureFlags; + + private: + virtual const ClassInfo* classInfo() const { return &info; } + + virtual JSValue getJSNumber(); + }; + + NumberObject* constructNumber(ExecState*, JSValue); + +} // namespace JSC + +#endif // NumberObject_h diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.cpp b/Source/JavaScriptCore/runtime/NumberPrototype.cpp new file mode 100644 index 0000000..0b86c00 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberPrototype.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "NumberPrototype.h" + +#include "Error.h" +#include "JSFunction.h" +#include "JSString.h" +#include "Operations.h" +#include "PrototypeFunction.h" +#include "dtoa.h" +#include <wtf/Assertions.h> +#include <wtf/DecimalNumber.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NumberPrototype); + +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*); + +// ECMA 15.7.4 + +NumberPrototype::NumberPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) + : NumberObject(structure) +{ + setInternalValue(jsNumber(0)); + + // The constructor will be added later, after NumberConstructor has been constructed + + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toString, numberProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, numberProtoFuncToLocaleString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, numberProtoFuncValueOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toFixed, numberProtoFuncToFixed), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toExponential, numberProtoFuncToExponential), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toPrecision, numberProtoFuncToPrecision), DontEnum); +} + +// ------------------------------ Functions --------------------------- + +// ECMA 15.7.4.2 - 15.7.4.7 + +static ALWAYS_INLINE bool toThisNumber(JSValue thisValue, double &x) +{ + JSValue v = thisValue.getJSNumber(); + if (UNLIKELY(!v)) + return false; + x = v.uncheckedGetNumber(); + return true; +} + +static ALWAYS_INLINE bool getIntegerArgumentInRange(ExecState* exec, int low, int high, int& result, bool& isUndefined) +{ + result = 0; + isUndefined = false; + + JSValue argument0 = exec->argument(0); + if (argument0.isUndefined()) { + isUndefined = true; + return true; + } + + double asDouble = argument0.toInteger(exec); + if (asDouble < low || asDouble > high) + return false; + + result = static_cast<int>(asDouble); + return true; +} + +// toExponential converts a number to a string, always formatting as an expoential. +// This method takes an optional argument specifying a number of *decimal places* +// to round the significand to (or, put another way, this method optionally rounds +// to argument-plus-one significant figures). +EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState* exec) +{ + // Get x (the double value of this, which should be a Number). + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + return throwVMTypeError(exec); + + // Get the argument. + int decimalPlacesInExponent; + bool isUndefined; + if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlacesInExponent, isUndefined)) + return throwVMError(exec, createRangeError(exec, "toExponential() argument must be between 0 and 20")); + + // Handle NaN and Infinity. + if (isnan(x) || isinf(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Round if the argument is not undefined, always format as exponential. + NumberToStringBuffer buffer; + unsigned length = isUndefined + ? DecimalNumber(x).toStringExponential(buffer, WTF::NumberToStringBufferLength) + : DecimalNumber(x, RoundingSignificantFigures, decimalPlacesInExponent + 1).toStringExponential(buffer, WTF::NumberToStringBufferLength); + + return JSValue::encode(jsString(exec, UString(buffer, length))); +} + +// toFixed converts a number to a string, always formatting as an a decimal fraction. +// This method takes an argument specifying a number of decimal places to round the +// significand to. However when converting large values (1e+21 and above) this +// method will instead fallback to calling ToString. +EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec) +{ + // Get x (the double value of this, which should be a Number). + JSValue thisValue = exec->hostThisValue(); + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + double x = v.uncheckedGetNumber(); + + // Get the argument. + int decimalPlaces; + bool isUndefined; // This is ignored; undefined treated as 0. + if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlaces, isUndefined)) + return throwVMError(exec, createRangeError(exec, "toFixed() argument must be between 0 and 20")); + + // 15.7.4.5.7 states "If x >= 10^21, then let m = ToString(x)" + // This also covers Ininity, and structure the check so that NaN + // values are also handled by numberToString + if (!(fabs(x) < 1e+21)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // The check above will return false for NaN or Infinity, these will be + // handled by numberToString. + ASSERT(!isnan(x) && !isinf(x)); + + // Convert to decimal with rounding, and format as decimal. + NumberToStringBuffer buffer; + unsigned length = DecimalNumber(x, RoundingDecimalPlaces, decimalPlaces).toStringDecimal(buffer, WTF::NumberToStringBufferLength); + return JSValue::encode(jsString(exec, UString(buffer, length))); +} + +// toPrecision converts a number to a string, takeing an argument specifying a +// number of significant figures to round the significand to. For positive +// exponent, all values that can be represented using a decimal fraction will +// be, e.g. when rounding to 3 s.f. any value up to 999 will be formated as a +// decimal, whilst 1000 is converted to the exponential representation 1.00e+3. +// For negative exponents values >= 1e-6 are formated as decimal fractions, +// with smaller values converted to exponential representation. +EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec) +{ + // Get x (the double value of this, which should be a Number). + JSValue thisValue = exec->hostThisValue(); + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + double x = v.uncheckedGetNumber(); + + // Get the argument. + int significantFigures; + bool isUndefined; + if (!getIntegerArgumentInRange(exec, 1, 21, significantFigures, isUndefined)) + return throwVMError(exec, createRangeError(exec, "toPrecision() argument must be between 1 and 21")); + + // To precision called with no argument is treated as ToString. + if (isUndefined) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Handle NaN and Infinity. + if (isnan(x) || isinf(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Convert to decimal with rounding. + DecimalNumber number(x, RoundingSignificantFigures, significantFigures); + // If number is in the range 1e-6 <= x < pow(10, significantFigures) then format + // as decimal. Otherwise, format the number as an exponential. Decimal format + // demands a minimum of (exponent + 1) digits to represent a number, for example + // 1234 (1.234e+3) requires 4 digits. (See ECMA-262 15.7.4.7.10.c) + NumberToStringBuffer buffer; + unsigned length = number.exponent() >= -6 && number.exponent() < significantFigures + ? number.toStringDecimal(buffer, WTF::NumberToStringBufferLength) + : number.toStringExponential(buffer, WTF::NumberToStringBufferLength); + return JSValue::encode(jsString(exec, UString(buffer, length))); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + + JSValue radixValue = exec->argument(0); + int radix; + if (radixValue.isInt32()) + radix = radixValue.asInt32(); + else if (radixValue.isUndefined()) + radix = 10; + else + radix = static_cast<int>(radixValue.toInteger(exec)); // nan -> 0 + + if (radix == 10) + return JSValue::encode(jsString(exec, v.toString(exec))); + + static const char* const digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + + // Fast path for number to character conversion. + if (radix == 36) { + if (v.isInt32()) { + int x = v.asInt32(); + if (static_cast<unsigned>(x) < 36) { // Exclude negatives + JSGlobalData* globalData = &exec->globalData(); + return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, digits[x])); + } + } + } + + if (radix < 2 || radix > 36) + return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36")); + + // INT_MAX results in 1024 characters left of the dot with radix 2 + // give the same space on the right side. safety checks are in place + // unless someone finds a precise rule. + char s[2048 + 3]; + const char* lastCharInString = s + sizeof(s) - 1; + double x = v.uncheckedGetNumber(); + if (isnan(x) || isinf(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + bool isNegative = x < 0.0; + if (isNegative) + x = -x; + + double integerPart = floor(x); + char* decimalPoint = s + sizeof(s) / 2; + + // convert integer portion + char* p = decimalPoint; + double d = integerPart; + do { + int remainderDigit = static_cast<int>(fmod(d, radix)); + *--p = digits[remainderDigit]; + d /= radix; + } while ((d <= -1.0 || d >= 1.0) && s < p); + + if (isNegative) + *--p = '-'; + char* startOfResultString = p; + ASSERT(s <= startOfResultString); + + d = x - integerPart; + p = decimalPoint; + const double epsilon = 0.001; // TODO: guessed. base on radix ? + bool hasFractionalPart = (d < -epsilon || d > epsilon); + if (hasFractionalPart) { + *p++ = '.'; + do { + d *= radix; + const int digit = static_cast<int>(d); + *p++ = digits[digit]; + d -= digit; + } while ((d < -epsilon || d > epsilon) && p < lastCharInString); + } + *p = '\0'; + ASSERT(p < s + sizeof(s)); + + return JSValue::encode(jsString(exec, startOfResultString)); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + // FIXME: Not implemented yet. + + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + + return JSValue::encode(jsString(exec, v.toString(exec))); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + + return JSValue::encode(v); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.h b/Source/JavaScriptCore/runtime/NumberPrototype.h new file mode 100644 index 0000000..78b690e --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberPrototype.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NumberPrototype_h +#define NumberPrototype_h + +#include "NumberObject.h" + +namespace JSC { + + class NumberPrototype : public NumberObject { + public: + NumberPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + }; + +} // namespace JSC + +#endif // NumberPrototype_h diff --git a/Source/JavaScriptCore/runtime/NumericStrings.h b/Source/JavaScriptCore/runtime/NumericStrings.h new file mode 100644 index 0000000..d65f142 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumericStrings.h @@ -0,0 +1,98 @@ +/* + * 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/FixedArray.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::number(d); + return entry.value; + } + + UString add(int i) + { + if (static_cast<unsigned>(i) < cacheSize) + return lookupSmallString(static_cast<unsigned>(i)); + CacheEntry<int>& entry = lookup(i); + if (i == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = i; + entry.value = UString::number(i); + return entry.value; + } + + UString add(unsigned i) + { + if (i < cacheSize) + return lookupSmallString(static_cast<unsigned>(i)); + CacheEntry<unsigned>& entry = lookup(i); + if (i == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = i; + entry.value = UString::number(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<unsigned>& lookup(unsigned i) { return unsignedCache[WTF::IntHash<unsigned>::hash(i) & (cacheSize - 1)]; } + const UString& lookupSmallString(unsigned i) + { + ASSERT(i < cacheSize); + if (smallIntCache[i].isNull()) + smallIntCache[i] = UString::number(i); + return smallIntCache[i]; + } + + FixedArray<CacheEntry<double>, cacheSize> doubleCache; + FixedArray<CacheEntry<int>, cacheSize> intCache; + FixedArray<CacheEntry<unsigned>, cacheSize> unsignedCache; + FixedArray<UString, cacheSize> smallIntCache; + }; + +} // namespace JSC + +#endif // NumericStrings_h diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp new file mode 100644 index 0000000..ca3dcd7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ObjectConstructor.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSFunction.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "ObjectPrototype.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.h" +#include "PrototypeFunction.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); + +static EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); + +ObjectConstructor::ObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) +: InternalFunction(&exec->globalData(), globalObject, 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(1), ReadOnly | DontEnum | DontDelete); + + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum); +} + +// ECMA 15.2.2 +static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& args) +{ + JSValue arg = args.at(0); + if (arg.isUndefinedOrNull()) + return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); + return arg.toObject(exec); +} + +static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructObject(exec, args)); +} + +ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithObjectConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructObject(exec, args)); +} + +CallType ObjectConstructor::getCallData(CallData& callData) +{ + callData.native.function = callObjectConstructor; + return CallTypeHost; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested prototype of a value that is not an object.")); + return JSValue::encode(asObject(exec->argument(0))->prototype()); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested property descriptor of a value that is not an object.")); + UString propertyName = exec->argument(1).toString(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + JSObject* object = asObject(exec->argument(0)); + PropertyDescriptor descriptor; + if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) + return JSValue::encode(jsUndefined()); + if (exec->hadException()) + return JSValue::encode(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 JSValue::encode(description); +} + +// FIXME: Use the enumeration cache. +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested property names of a value that is not an object.")); + PropertyNameArray properties(exec); + asObject(exec->argument(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties); + JSArray* names = constructEmptyArray(exec); + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) + names->push(exec, jsOwnedString(exec, properties[i].ustring())); + return JSValue::encode(names); +} + +// FIXME: Use the enumeration cache. +EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested keys of a value that is not an object.")); + PropertyNameArray properties(exec); + asObject(exec->argument(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 JSValue::encode(keys); +} + +// ES5 8.10.5 ToPropertyDescriptor +static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) +{ + if (!in.isObject()) { + throwError(exec, createTypeError(exec, "Property description must be an object.")); + return false; + } + JSObject* description = asObject(in); + + PropertySlot enumerableSlot(description); + 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(description); + 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(description); + if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { + desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); + if (exec->hadException()) + return false; + } + + PropertySlot writableSlot(description); + 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(description); + 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 (getCallData(get, callData) == CallTypeNone) { + throwError(exec, createTypeError(exec, "Getter must be a function.")); + return false; + } + } else + get = JSValue(); + desc.setGetter(get); + } + + PropertySlot setSlot(description); + 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 (getCallData(set, callData) == CallTypeNone) { + throwError(exec, createTypeError(exec, "Setter must be a function.")); + return false; + } + } else + set = JSValue(); + + desc.setSetter(set); + } + + if (!desc.isAccessorDescriptor()) + return true; + + if (desc.value()) { + throwError(exec, createTypeError(exec, "Invalid property. 'value' present on property with getter or setter.")); + return false; + } + + if (desc.writablePresent()) { + throwError(exec, createTypeError(exec, "Invalid property. 'writable' present on property with getter or setter.")); + return false; + } + return true; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects.")); + JSObject* O = asObject(exec->argument(0)); + UString propertyName = exec->argument(1).toString(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + PropertyDescriptor descriptor; + if (!toPropertyDescriptor(exec, exec->argument(2), descriptor)) + return JSValue::encode(jsNull()); + ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); + ASSERT(!exec->hadException()); + O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true); + return JSValue::encode(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; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects.")); + if (!exec->argument(1).isObject()) + return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object.")); + return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), asObject(exec->argument(1)))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec) +{ + if (!exec->argument(0).isObject() && !exec->argument(0).isNull()) + return throwVMError(exec, createTypeError(exec, "Object prototype may only be an Object or null.")); + JSObject* newObject = constructEmptyObject(exec); + newObject->setPrototype(exec->argument(0)); + if (exec->argument(1).isUndefined()) + return JSValue::encode(newObject); + if (!exec->argument(1).isObject()) + return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object.")); + return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1)))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.h b/Source/JavaScriptCore/runtime/ObjectConstructor.h new file mode 100644 index 0000000..04a3c1a --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ObjectConstructor_h +#define ObjectConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class ObjectPrototype; + + class ObjectConstructor : public InternalFunction { + public: + ObjectConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, ObjectPrototype*, Structure* prototypeFunctionStructure); + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + +} // namespace JSC + +#endif // ObjectConstructor_h diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp new file mode 100644 index 0000000..57a8a31 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ObjectPrototype.h" + +#include "Error.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "PrototypeFunction.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype); + +static EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*); + +ObjectPrototype::ObjectPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> stucture, Structure* prototypeFunctionStructure) + : JSObject(stucture) + , m_hasNoPropertiesWithUInt32Names(true) +{ + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, objectProtoFuncValueOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().hasOwnProperty, objectProtoFuncHasOwnProperty), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().isPrototypeOf, objectProtoFuncIsPrototypeOf), DontEnum); + + // Mozilla extensions + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().__defineGetter__, objectProtoFuncDefineGetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().__defineSetter__, objectProtoFuncDefineSetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().__lookupGetter__, objectProtoFuncLookupGetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, 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.toUInt32(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 + +EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisObject(exec)); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsBoolean(thisValue.toThisObject(exec)->hasOwnProperty(exec, Identifier(exec, exec->argument(0).toString(exec))))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSObject* thisObj = thisValue.toThisObject(exec); + + if (!exec->argument(0).isObject()) + return JSValue::encode(jsBoolean(false)); + + JSValue v = asObject(exec->argument(0))->prototype(); + + while (true) { + if (!v.isObject()) + return JSValue::encode(jsBoolean(false)); + if (v == thisObj) + return JSValue::encode(jsBoolean(true)); + v = asObject(v)->prototype(); + } +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + CallData callData; + if (getCallData(exec->argument(1), callData) == CallTypeNone) + return throwVMError(exec, createSyntaxError(exec, "invalid getter usage")); + thisValue.toThisObject(exec)->defineGetter(exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1))); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + CallData callData; + if (getCallData(exec->argument(1), callData) == CallTypeNone) + return throwVMError(exec, createSyntaxError(exec, "invalid setter usage")); + thisValue.toThisObject(exec)->defineSetter(exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1))); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisObject(exec)->lookupGetter(exec, Identifier(exec, exec->argument(0).toString(exec)))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisObject(exec)->lookupSetter(exec, Identifier(exec, exec->argument(0).toString(exec)))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsBoolean(thisValue.toThisObject(exec)->propertyIsEnumerable(exec, Identifier(exec, exec->argument(0).toString(exec))))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisJSString(exec)); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisValue.toThisObject(exec)->className(), "]")); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.h b/Source/JavaScriptCore/runtime/ObjectPrototype.h new file mode 100644 index 0000000..0382ae4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ObjectPrototype_h +#define ObjectPrototype_h + +#include "JSObject.h" + +namespace JSC { + + class ObjectPrototype : public JSObject { + public: + ObjectPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + + private: + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + + bool m_hasNoPropertiesWithUInt32Names; + }; + + EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState*); + +} // namespace JSC + +#endif // ObjectPrototype_h diff --git a/Source/JavaScriptCore/runtime/Operations.cpp b/Source/JavaScriptCore/runtime/Operations.cpp new file mode 100644 index 0000000..f129a80 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Operations.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 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 + * 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 "Operations.h" + +#include "Error.h" +#include "JSObject.h" +#include "JSString.h" +#include <math.h> +#include <stdio.h> +#include <wtf/MathExtras.h> + +namespace JSC { + +bool JSValue::equalSlowCase(ExecState* exec, JSValue v1, JSValue v2) +{ + return equalSlowCaseInline(exec, v1, v2); +} + +bool JSValue::strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2) +{ + return strictEqualSlowCaseInline(exec, v1, v2); +} + +NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + // exception for the Date exception in defaultValue() + JSValue p1 = v1.toPrimitive(callFrame); + JSValue p2 = v2.toPrimitive(callFrame); + + if (p1.isString()) { + return p2.isString() + ? jsString(callFrame, asString(p1), asString(p2)) + : jsString(callFrame, asString(p1), p2.toString(callFrame)); + } + if (p2.isString()) + return jsString(callFrame, p1.toString(callFrame), asString(p2)); + + return jsNumber(p1.toNumber(callFrame) + p2.toNumber(callFrame)); +} + +JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v) +{ + if (v.isUndefined()) + return jsNontrivialString(callFrame, "undefined"); + if (v.isBoolean()) + return jsNontrivialString(callFrame, "boolean"); + if (v.isNumber()) + return jsNontrivialString(callFrame, "number"); + if (v.isString()) + return jsNontrivialString(callFrame, "string"); + if (v.isObject()) { + // Return "undefined" for objects that should be treated + // as null when doing comparisons. + if (asObject(v)->structure()->typeInfo().masqueradesAsUndefined()) + return jsNontrivialString(callFrame, "undefined"); + CallData callData; + if (asObject(v)->getCallData(callData) != CallTypeNone) + return jsNontrivialString(callFrame, "function"); + } + return jsNontrivialString(callFrame, "object"); +} + +bool jsIsObjectType(JSValue v) +{ + if (!v.isCell()) + return v.isNull(); + + JSType type = v.asCell()->structure()->typeInfo().type(); + if (type == NumberType || type == StringType) + return false; + if (type == ObjectType) { + if (asObject(v)->structure()->typeInfo().masqueradesAsUndefined()) + return false; + CallData callData; + if (asObject(v)->getCallData(callData) != CallTypeNone) + return false; + } + return true; +} + +bool jsIsFunctionType(JSValue v) +{ + if (v.isObject()) { + CallData callData; + if (asObject(v)->getCallData(callData) != CallTypeNone) + return true; + } + return false; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h new file mode 100644 index 0000000..1252345 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2002, 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 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. + * + */ + +#ifndef Operations_h +#define Operations_h + +#include "ExceptionHelpers.h" +#include "Interpreter.h" +#include "JSImmediate.h" +#include "JSNumberCell.h" +#include "JSString.h" + +namespace JSC { + + NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); + JSValue jsTypeStringForValue(CallFrame*, JSValue); + bool jsIsObjectType(JSValue); + bool jsIsFunctionType(JSValue); + + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) + { + unsigned length1 = s1->length(); + if (!length1) + return s2; + unsigned length2 = s2->length(); + if (!length2) + return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = s1->fiberCount() + s2->fiberCount(); + JSGlobalData* globalData = &exec->globalData(); + + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, s1, s2); + + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + ropeBuilder.append(s1); + ropeBuilder.append(s2); + return new (globalData) JSString(globalData, ropeBuilder.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) + { + unsigned length1 = u1.length(); + if (!length1) + return s2; + unsigned length2 = s2->length(); + if (!length2) + return jsString(exec, u1); + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = 1 + s2->fiberCount(); + JSGlobalData* globalData = &exec->globalData(); + + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, u1, s2); + + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + ropeBuilder.append(u1); + ropeBuilder.append(s2); + return new (globalData) JSString(globalData, ropeBuilder.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) + { + unsigned length1 = s1->length(); + if (!length1) + return jsString(exec, u2); + unsigned length2 = u2.length(); + if (!length2) + return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = s1->fiberCount() + 1; + JSGlobalData* globalData = &exec->globalData(); + + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, s1, u2); + + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + ropeBuilder.append(s1); + ropeBuilder.append(u2); + return new (globalData) JSString(globalData, ropeBuilder.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2) + { + unsigned length1 = u1.length(); + if (!length1) + return jsString(exec, u2); + unsigned length2 = u2.length(); + if (!length2) + return jsString(exec, u1); + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + JSGlobalData* globalData = &exec->globalData(); + return new (globalData) JSString(globalData, u1, u2); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3) + { + unsigned length1 = u1.length(); + unsigned length2 = u2.length(); + unsigned length3 = u3.length(); + if (!length1) + return jsString(exec, u2, u3); + if (!length2) + return jsString(exec, u1, u3); + if (!length3) + return jsString(exec, u1, u2); + + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + if ((length1 + length2 + length3) < length3) + return throwOutOfMemoryError(exec); + + JSGlobalData* globalData = &exec->globalData(); + return new (globalData) JSString(globalData, u1, u2, u3); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) + { + ASSERT(count >= 3); + + unsigned fiberCount = 0; + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (LIKELY(v.isString())) + fiberCount += asString(v)->fiberCount(); + else + ++fiberCount; + } + + JSGlobalData* globalData = &exec->globalData(); + if (fiberCount == 3) + return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); + + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + + unsigned length = 0; + bool overflow = false; + + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (LIKELY(v.isString())) + ropeBuilder.append(asString(v)); + else + ropeBuilder.append(v.toString(exec)); + + unsigned newLength = ropeBuilder.length(); + if (newLength < length) + overflow = true; + length = newLength; + } + + if (overflow) + return throwOutOfMemoryError(exec); + + return new (globalData) JSString(globalData, ropeBuilder.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue) + { + unsigned fiberCount = 0; + if (LIKELY(thisValue.isString())) + fiberCount += asString(thisValue)->fiberCount(); + else + ++fiberCount; + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); + if (LIKELY(v.isString())) + fiberCount += asString(v)->fiberCount(); + else + ++fiberCount; + } + + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + + if (LIKELY(thisValue.isString())) + ropeBuilder.append(asString(thisValue)); + else + ropeBuilder.append(thisValue.toString(exec)); + + unsigned length = 0; + bool overflow = false; + + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); + if (LIKELY(v.isString())) + ropeBuilder.append(asString(v)); + else + ropeBuilder.append(v.toString(exec)); + + unsigned newLength = ropeBuilder.length(); + if (newLength < length) + overflow = true; + length = newLength; + } + + if (overflow) + return throwOutOfMemoryError(exec); + + JSGlobalData* globalData = &exec->globalData(); + return new (globalData) JSString(globalData, ropeBuilder.release()); + } + + // ECMA 11.9.3 + inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) + { + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + + return equalSlowCase(exec, v1, v2); + } + + ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) + { + do { + if (v1.isNumber() && v2.isNumber()) + return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); + + bool s1 = v1.isString(); + bool s2 = v2.isString(); + if (s1 && s2) + return asString(v1)->value(exec) == asString(v2)->value(exec); + + if (v1.isUndefinedOrNull()) { + if (v2.isUndefinedOrNull()) + return true; + if (!v2.isCell()) + return false; + return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined(); + } + + if (v2.isUndefinedOrNull()) { + if (!v1.isCell()) + return false; + return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined(); + } + + if (v1.isObject()) { + if (v2.isObject()) + return v1 == v2; + JSValue p1 = v1.toPrimitive(exec); + if (exec->hadException()) + return false; + v1 = p1; + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + continue; + } + + if (v2.isObject()) { + JSValue p2 = v2.toPrimitive(exec); + if (exec->hadException()) + return false; + v2 = p2; + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + continue; + } + + if (s1 || s2) { + double d1 = v1.toNumber(exec); + double d2 = v2.toNumber(exec); + return d1 == d2; + } + + if (v1.isBoolean()) { + if (v2.isNumber()) + return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber(); + } else if (v2.isBoolean()) { + if (v1.isNumber()) + return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean()); + } + + return v1 == v2; + } while (true); + } + + // ECMA 11.9.3 + ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) + { + ASSERT(v1.isCell() && v2.isCell()); + + if (v1.asCell()->isString() && v2.asCell()->isString()) + return asString(v1)->value(exec) == asString(v2)->value(exec); + + return v1 == v2; + } + + inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) + { + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + + if (v1.isNumber() && v2.isNumber()) + return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); + + if (!v1.isCell() || !v2.isCell()) + return v1 == v2; + + return strictEqualSlowCaseInline(exec, v1, v2); + } + + ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) + { + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() < v2.asInt32(); + + double n1; + double n2; + if (v1.getNumber(n1) && v2.getNumber(n2)) + return n1 < n2; + + JSGlobalData* globalData = &callFrame->globalData(); + if (isJSString(globalData, v1) && isJSString(globalData, v2)) + return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); + + JSValue p1; + JSValue p2; + bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + + if (wasNotString1 | wasNotString2) + return n1 < n2; + + return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); + } + + inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) + { + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() <= v2.asInt32(); + + double n1; + double n2; + if (v1.getNumber(n1) && v2.getNumber(n2)) + return n1 <= n2; + + JSGlobalData* globalData = &callFrame->globalData(); + if (isJSString(globalData, v1) && isJSString(globalData, v2)) + return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); + + JSValue p1; + JSValue p2; + bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + + if (wasNotString1 | wasNotString2) + return n1 <= n2; + + return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); + } + + // Fast-path choices here are based on frequency data from SunSpider: + // <times> Add case: <t1> <t2> + // --------------------------- + // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) + // 247412 Add case: 5 5 + // 20900 Add case: 5 6 + // 13962 Add case: 5 3 + // 4000 Add case: 3 5 + + ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) + { + double left = 0.0, right; + if (v1.getNumber(left) && v2.getNumber(right)) + return jsNumber(left + right); + + if (v1.isString()) { + return v2.isString() + ? jsString(callFrame, asString(v1), asString(v2)) + : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame)); + } + + // All other cases are pretty uncommon + return jsAddSlowCase(callFrame, v1, v2); + } + + inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset) + { + JSCell* cell = base.asCell(); + size_t count = 0; + + while (slotBase != cell) { + JSValue v = cell->structure()->prototypeForLookup(callFrame); + + // If we didn't find slotBase in base's prototype chain, then base + // must be a proxy for another object. + + if (v.isNull()) + return 0; + + cell = v.asCell(); + + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (cell->structure()->isDictionary()) { + asObject(cell)->flattenDictionaryObject(); + if (slotBase == cell) + slotOffset = cell->structure()->get(propertyName); + } + + ++count; + } + + ASSERT(count); + return count; + } + + inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base) + { + size_t count = 0; + while (1) { + JSValue v = base->structure()->prototypeForLookup(callFrame); + if (v.isNull()) + return count; + + base = v.asCell(); + + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (base->structure()->isDictionary()) + asObject(base)->flattenDictionaryObject(); + + ++count; + } + } + + ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut) + { + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator next = iter; + ++next; + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + while (true) { + base = *iter; + if (next == end) + return isStrictPut ? JSValue() : base; + if (base->getPropertySlot(callFrame, property, slot)) + return base; + + iter = next; + ++next; + } + + ASSERT_NOT_REACHED(); + return JSValue(); + } +} // namespace JSC + +#endif // Operations_h diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp new file mode 100644 index 0000000..558ae28 --- /dev/null +++ b/Source/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(ExecState* exec, 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(exec, other.m_value, m_value)) && + (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter)) && + (!m_setter || JSValue::strictEqual(exec, 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/Source/JavaScriptCore/runtime/PropertyDescriptor.h b/Source/JavaScriptCore/runtime/PropertyDescriptor.h new file mode 100644 index 0000000..ff9f160 --- /dev/null +++ b/Source/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(ExecState* exec, 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/Source/JavaScriptCore/runtime/PropertyMapHashTable.h b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h new file mode 100644 index 0000000..bd452b6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 + * 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. + * + */ + +#ifndef PropertyMapHashTable_h +#define PropertyMapHashTable_h + +#include "UString.h" +#include <wtf/Vector.h> + +namespace JSC { + + struct PropertyMapEntry { + StringImpl* key; + unsigned offset; + unsigned attributes; + JSCell* specificValue; + unsigned index; + + PropertyMapEntry(StringImpl* key, unsigned attributes, JSCell* specificValue) + : key(key) + , offset(0) + , attributes(attributes) + , specificValue(specificValue) + , index(0) + { + } + + PropertyMapEntry(StringImpl* key, unsigned offset, unsigned attributes, JSCell* specificValue, unsigned index) + : key(key) + , offset(offset) + , attributes(attributes) + , specificValue(specificValue) + , index(index) + { + } + }; + + // lastIndexUsed is an ever-increasing index used to identify the order items + // were inserted into the property map. It's required that getEnumerablePropertyNames + // return the properties in the order they were added for compatibility with other + // browsers' JavaScript implementations. + struct PropertyMapHashTable { + unsigned sizeMask; + unsigned size; + unsigned keyCount; + unsigned deletedSentinelCount; + unsigned lastIndexUsed; + Vector<unsigned>* deletedOffsets; + unsigned entryIndices[1]; + + PropertyMapEntry* entries() + { + // The entries vector comes after the indices vector. + // The 0th item in the entries vector is not really used; it has to + // have a 0 in its key to allow the hash table lookup to handle deleted + // sentinels without any special-case code, but the other fields are unused. + return reinterpret_cast<PropertyMapEntry*>(&entryIndices[size]); + } + + static size_t allocationSize(unsigned size) + { + // We never let a hash table get more than half full, + // So the number of indices we need is the size of the hash table. + // But the number of entries is half that (plus one for the deleted sentinel). + return sizeof(PropertyMapHashTable) + + (size - 1) * sizeof(unsigned) + + (1 + size / 2) * sizeof(PropertyMapEntry); + } + }; + +} // namespace JSC + +#endif // PropertyMapHashTable_h diff --git a/Source/JavaScriptCore/runtime/PropertyNameArray.cpp b/Source/JavaScriptCore/runtime/PropertyNameArray.cpp new file mode 100644 index 0000000..afb41be --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyNameArray.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2006, 2008 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 + * 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 "PropertyNameArray.h" + +#include "Structure.h" +#include "StructureChain.h" + +namespace JSC { + +static const size_t setThreshold = 20; + +void PropertyNameArray::add(StringImpl* identifier) +{ + ASSERT(!identifier || identifier == StringImpl::empty() || identifier->isIdentifier()); + + size_t size = m_data->propertyNameVector().size(); + if (size < setThreshold) { + for (size_t i = 0; i < size; ++i) { + if (identifier == m_data->propertyNameVector()[i].impl()) + return; + } + } else { + if (m_set.isEmpty()) { + for (size_t i = 0; i < size; ++i) + m_set.add(m_data->propertyNameVector()[i].impl()); + } + if (!m_set.add(identifier).second) + return; + } + + addKnownUnique(identifier); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PropertyNameArray.h b/Source/JavaScriptCore/runtime/PropertyNameArray.h new file mode 100644 index 0000000..0da930f --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyNameArray.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2006, 2008 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 + * 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. + * + */ + +#ifndef PropertyNameArray_h +#define PropertyNameArray_h + +#include "CallFrame.h" +#include "Identifier.h" +#include <wtf/HashSet.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/Vector.h> + +namespace JSC { + + class Structure; + class StructureChain; + + // FIXME: Rename to PropertyNameArray. + class PropertyNameArrayData : public RefCounted<PropertyNameArrayData> { + public: + typedef Vector<Identifier, 20> PropertyNameVector; + + static PassRefPtr<PropertyNameArrayData> create() { return adoptRef(new PropertyNameArrayData); } + + PropertyNameVector& propertyNameVector() { return m_propertyNameVector; } + + private: + PropertyNameArrayData() + { + } + + PropertyNameVector m_propertyNameVector; + }; + + // FIXME: Rename to PropertyNameArrayBuilder. + class PropertyNameArray { + public: + PropertyNameArray(JSGlobalData* globalData) + : m_data(PropertyNameArrayData::create()) + , m_globalData(globalData) + , m_shouldCache(true) + { + } + + PropertyNameArray(ExecState* exec) + : m_data(PropertyNameArrayData::create()) + , m_globalData(&exec->globalData()) + , m_shouldCache(true) + { + } + + JSGlobalData* globalData() { return m_globalData; } + + void add(const Identifier& identifier) { add(identifier.impl()); } + void add(StringImpl*); + void addKnownUnique(StringImpl* identifier) { m_data->propertyNameVector().append(Identifier(m_globalData, identifier)); } + + Identifier& operator[](unsigned i) { return m_data->propertyNameVector()[i]; } + const Identifier& operator[](unsigned i) const { return m_data->propertyNameVector()[i]; } + + void setData(PassRefPtr<PropertyNameArrayData> data) { m_data = data; } + PropertyNameArrayData* data() { return m_data.get(); } + PassRefPtr<PropertyNameArrayData> releaseData() { return m_data.release(); } + + // FIXME: Remove these functions. + typedef PropertyNameArrayData::PropertyNameVector::const_iterator const_iterator; + size_t size() const { return m_data->propertyNameVector().size(); } + const_iterator begin() const { return m_data->propertyNameVector().begin(); } + const_iterator end() const { return m_data->propertyNameVector().end(); } + + private: + typedef HashSet<StringImpl*, PtrHash<StringImpl*> > IdentifierSet; + + RefPtr<PropertyNameArrayData> m_data; + IdentifierSet m_set; + JSGlobalData* m_globalData; + bool m_shouldCache; + }; + +} // namespace JSC + +#endif // PropertyNameArray_h diff --git a/Source/JavaScriptCore/runtime/PropertySlot.cpp b/Source/JavaScriptCore/runtime/PropertySlot.cpp new file mode 100644 index 0000000..fd16c0c --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertySlot.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005, 2008 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 + * 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 "PropertySlot.h" + +#include "JSFunction.h" +#include "JSGlobalObject.h" + +namespace JSC { + +JSValue PropertySlot::functionGetter(ExecState* exec) const +{ + // Prevent getter functions from observing execution if an exception is pending. + if (exec->hadException()) + return exec->exception(); + + CallData callData; + CallType callType = m_data.getterFunc->getCallData(callData); + return call(exec, m_data.getterFunc, callType, callData, thisValue(), exec->emptyList()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PropertySlot.h b/Source/JavaScriptCore/runtime/PropertySlot.h new file mode 100644 index 0000000..de9ddc9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertySlot.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005, 2007, 2008 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 + * 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. + * + */ + +#ifndef PropertySlot_h +#define PropertySlot_h + +#include "Identifier.h" +#include "JSValue.h" +#include "Register.h" +#include <wtf/Assertions.h> +#include <wtf/NotFound.h> + +namespace JSC { + + class ExecState; + class JSObject; + +#define JSC_VALUE_SLOT_MARKER 0 +#define JSC_REGISTER_SLOT_MARKER reinterpret_cast<GetValueFunc>(1) +#define INDEX_GETTER_MARKER reinterpret_cast<GetValueFunc>(2) +#define GETTER_FUNCTION_MARKER reinterpret_cast<GetValueFunc>(3) + + class PropertySlot { + public: + enum CachedPropertyType { + Uncacheable, + Getter, + Custom, + Value + }; + + PropertySlot() + : m_cachedPropertyType(Uncacheable) + { + clearBase(); + clearOffset(); + clearValue(); + } + + explicit PropertySlot(const JSValue base) + : m_slotBase(base) + , m_cachedPropertyType(Uncacheable) + { + clearOffset(); + clearValue(); + } + + typedef JSValue (*GetValueFunc)(ExecState*, JSValue slotBase, const Identifier&); + typedef JSValue (*GetIndexValueFunc)(ExecState*, JSValue slotBase, unsigned); + + JSValue getValue(ExecState* exec, const Identifier& propertyName) const + { + if (m_getValue == JSC_VALUE_SLOT_MARKER) + return *m_data.valueSlot; + if (m_getValue == JSC_REGISTER_SLOT_MARKER) + return (*m_data.registerSlot).jsValue(); + if (m_getValue == INDEX_GETTER_MARKER) + return m_getIndexValue(exec, slotBase(), index()); + if (m_getValue == GETTER_FUNCTION_MARKER) + return functionGetter(exec); + return m_getValue(exec, slotBase(), propertyName); + } + + JSValue getValue(ExecState* exec, unsigned propertyName) const + { + if (m_getValue == JSC_VALUE_SLOT_MARKER) + return *m_data.valueSlot; + if (m_getValue == JSC_REGISTER_SLOT_MARKER) + return (*m_data.registerSlot).jsValue(); + if (m_getValue == INDEX_GETTER_MARKER) + return m_getIndexValue(exec, m_slotBase, m_data.index); + if (m_getValue == GETTER_FUNCTION_MARKER) + return functionGetter(exec); + return m_getValue(exec, slotBase(), Identifier::from(exec, propertyName)); + } + + CachedPropertyType cachedPropertyType() const { return m_cachedPropertyType; } + bool isCacheable() const { return m_cachedPropertyType != Uncacheable; } + bool isCacheableValue() const { return m_cachedPropertyType == Value; } + size_t cachedOffset() const + { + ASSERT(isCacheable()); + return m_offset; + } + + void setValueSlot(JSValue* valueSlot) + { + ASSERT(valueSlot); + clearBase(); + clearOffset(); + m_getValue = JSC_VALUE_SLOT_MARKER; + m_data.valueSlot = valueSlot; + } + + void setValueSlot(JSValue slotBase, JSValue* valueSlot) + { + ASSERT(valueSlot); + m_getValue = JSC_VALUE_SLOT_MARKER; + m_slotBase = slotBase; + m_data.valueSlot = valueSlot; + } + + void setValueSlot(JSValue slotBase, JSValue* valueSlot, size_t offset) + { + ASSERT(valueSlot); + m_getValue = JSC_VALUE_SLOT_MARKER; + m_slotBase = slotBase; + m_data.valueSlot = valueSlot; + m_offset = offset; + m_cachedPropertyType = Value; + } + + void setValue(JSValue value) + { + ASSERT(value); + clearBase(); + clearOffset(); + m_getValue = JSC_VALUE_SLOT_MARKER; + m_value = value; + m_data.valueSlot = &m_value; + } + + void setRegisterSlot(Register* registerSlot) + { + ASSERT(registerSlot); + clearBase(); + clearOffset(); + m_getValue = JSC_REGISTER_SLOT_MARKER; + m_data.registerSlot = registerSlot; + } + + void setCustom(JSValue slotBase, GetValueFunc getValue) + { + ASSERT(slotBase); + ASSERT(getValue); + m_getValue = getValue; + m_getIndexValue = 0; + m_slotBase = slotBase; + } + + void setCacheableCustom(JSValue slotBase, GetValueFunc getValue) + { + ASSERT(slotBase); + ASSERT(getValue); + m_getValue = getValue; + m_getIndexValue = 0; + m_slotBase = slotBase; + m_cachedPropertyType = Custom; + } + + void setCustomIndex(JSValue slotBase, unsigned index, GetIndexValueFunc getIndexValue) + { + ASSERT(slotBase); + ASSERT(getIndexValue); + m_getValue = INDEX_GETTER_MARKER; + m_getIndexValue = getIndexValue; + m_slotBase = slotBase; + m_data.index = index; + } + + void setGetterSlot(JSObject* getterFunc) + { + ASSERT(getterFunc); + m_thisValue = m_slotBase; + m_getValue = GETTER_FUNCTION_MARKER; + m_data.getterFunc = getterFunc; + } + + void setCacheableGetterSlot(JSValue slotBase, JSObject* getterFunc, unsigned offset) + { + ASSERT(getterFunc); + m_getValue = GETTER_FUNCTION_MARKER; + m_thisValue = m_slotBase; + m_slotBase = slotBase; + m_data.getterFunc = getterFunc; + m_offset = offset; + m_cachedPropertyType = Getter; + } + + void setUndefined() + { + setValue(jsUndefined()); + } + + JSValue slotBase() const + { + return m_slotBase; + } + + void setBase(JSValue base) + { + ASSERT(m_slotBase); + ASSERT(base); + m_slotBase = base; + } + + void clearBase() + { +#ifndef NDEBUG + m_slotBase = JSValue(); +#endif + } + + void clearValue() + { +#ifndef NDEBUG + m_value = JSValue(); +#endif + } + + void clearOffset() + { + // Clear offset even in release builds, in case this PropertySlot has been used before. + // (For other data members, we don't need to clear anything because reuse would meaningfully overwrite them.) + m_offset = 0; + m_cachedPropertyType = Uncacheable; + } + + unsigned index() const { return m_data.index; } + + JSValue thisValue() const { return m_thisValue; } + + GetValueFunc customGetter() const + { + ASSERT(m_cachedPropertyType == Custom); + return m_getValue; + } + private: + JSValue functionGetter(ExecState*) const; + + GetValueFunc m_getValue; + GetIndexValueFunc m_getIndexValue; + + JSValue m_slotBase; + union { + JSObject* getterFunc; + JSValue* valueSlot; + Register* registerSlot; + unsigned index; + } m_data; + + JSValue m_value; + JSValue m_thisValue; + + size_t m_offset; + CachedPropertyType m_cachedPropertyType; + }; + +} // namespace JSC + +#endif // PropertySlot_h diff --git a/Source/JavaScriptCore/runtime/Protect.h b/Source/JavaScriptCore/runtime/Protect.h new file mode 100644 index 0000000..06cf97f --- /dev/null +++ b/Source/JavaScriptCore/runtime/Protect.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2004, 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 + * 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. + * + */ + + +#ifndef Protect_h +#define Protect_h + +#include "Collector.h" +#include "JSValue.h" + +namespace JSC { + + inline void gcProtect(JSCell* val) + { + Heap::heap(val)->protect(val); + } + + inline void gcUnprotect(JSCell* val) + { + Heap::heap(val)->unprotect(val); + } + + inline void gcProtectNullTolerant(JSCell* val) + { + if (val) + gcProtect(val); + } + + inline void gcUnprotectNullTolerant(JSCell* val) + { + if (val) + gcUnprotect(val); + } + + inline void gcProtect(JSValue value) + { + if (value && value.isCell()) + gcProtect(value.asCell()); + } + + inline void gcUnprotect(JSValue value) + { + if (value && value.isCell()) + gcUnprotect(value.asCell()); + } + + // FIXME: Share more code with RefPtr template? The only differences are the ref/deref operation + // and the implicit conversion to raw pointer + template <class T> class ProtectedPtr { + public: + ProtectedPtr() : m_ptr(0) {} + ProtectedPtr(T* ptr); + ProtectedPtr(const ProtectedPtr&); + ~ProtectedPtr(); + + template <class U> ProtectedPtr(const ProtectedPtr<U>&); + + T* get() const { return m_ptr; } + operator T*() const { return m_ptr; } + operator JSValue() const { return JSValue(m_ptr); } + T* operator->() const { return m_ptr; } + + operator bool() const { return m_ptr; } + bool operator!() const { return !m_ptr; } + + ProtectedPtr& operator=(const ProtectedPtr&); + ProtectedPtr& operator=(T*); + + private: + T* m_ptr; + }; + + class ProtectedJSValue { + public: + ProtectedJSValue() {} + ProtectedJSValue(JSValue value); + ProtectedJSValue(const ProtectedJSValue&); + ~ProtectedJSValue(); + + template <class U> ProtectedJSValue(const ProtectedPtr<U>&); + + JSValue get() const { return m_value; } + operator JSValue() const { return m_value; } + JSValue operator->() const { return m_value; } + + operator bool() const { return m_value; } + bool operator!() const { return !m_value; } + + ProtectedJSValue& operator=(const ProtectedJSValue&); + ProtectedJSValue& operator=(JSValue); + + private: + JSValue m_value; + }; + + template <class T> inline ProtectedPtr<T>::ProtectedPtr(T* ptr) + : m_ptr(ptr) + { + gcProtectNullTolerant(m_ptr); + } + + template <class T> inline ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr& o) + : m_ptr(o.get()) + { + gcProtectNullTolerant(m_ptr); + } + + template <class T> inline ProtectedPtr<T>::~ProtectedPtr() + { + gcUnprotectNullTolerant(m_ptr); + } + + template <class T> template <class U> inline ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr<U>& o) + : m_ptr(o.get()) + { + gcProtectNullTolerant(m_ptr); + } + + template <class T> inline ProtectedPtr<T>& ProtectedPtr<T>::operator=(const ProtectedPtr<T>& o) + { + T* optr = o.m_ptr; + gcProtectNullTolerant(optr); + gcUnprotectNullTolerant(m_ptr); + m_ptr = optr; + return *this; + } + + template <class T> inline ProtectedPtr<T>& ProtectedPtr<T>::operator=(T* optr) + { + gcProtectNullTolerant(optr); + gcUnprotectNullTolerant(m_ptr); + m_ptr = optr; + return *this; + } + + inline ProtectedJSValue::ProtectedJSValue(JSValue value) + : m_value(value) + { + gcProtect(m_value); + } + + inline ProtectedJSValue::ProtectedJSValue(const ProtectedJSValue& o) + : m_value(o.get()) + { + gcProtect(m_value); + } + + inline ProtectedJSValue::~ProtectedJSValue() + { + gcUnprotect(m_value); + } + + template <class U> ProtectedJSValue::ProtectedJSValue(const ProtectedPtr<U>& o) + : m_value(o.get()) + { + gcProtect(m_value); + } + + inline ProtectedJSValue& ProtectedJSValue::operator=(const ProtectedJSValue& o) + { + JSValue ovalue = o.m_value; + gcProtect(ovalue); + gcUnprotect(m_value); + m_value = ovalue; + return *this; + } + + inline ProtectedJSValue& ProtectedJSValue::operator=(JSValue ovalue) + { + gcProtect(ovalue); + gcUnprotect(m_value); + m_value = ovalue; + return *this; + } + + template <class T> inline bool operator==(const ProtectedPtr<T>& a, const ProtectedPtr<T>& b) { return a.get() == b.get(); } + template <class T> inline bool operator==(const ProtectedPtr<T>& a, const T* b) { return a.get() == b; } + template <class T> inline bool operator==(const T* a, const ProtectedPtr<T>& b) { return a == b.get(); } + + template <class T> inline bool operator!=(const ProtectedPtr<T>& a, const ProtectedPtr<T>& b) { return a.get() != b.get(); } + template <class T> inline bool operator!=(const ProtectedPtr<T>& a, const T* b) { return a.get() != b; } + template <class T> inline bool operator!=(const T* a, const ProtectedPtr<T>& b) { return a != b.get(); } + + inline bool operator==(const ProtectedJSValue& a, const ProtectedJSValue& b) { return a.get() == b.get(); } + inline bool operator==(const ProtectedJSValue& a, const JSValue b) { return a.get() == b; } + template <class T> inline bool operator==(const ProtectedJSValue& a, const ProtectedPtr<T>& b) { return a.get() == JSValue(b.get()); } + inline bool operator==(const JSValue a, const ProtectedJSValue& b) { return a == b.get(); } + template <class T> inline bool operator==(const ProtectedPtr<T>& a, const ProtectedJSValue& b) { return JSValue(a.get()) == b.get(); } + + inline bool operator!=(const ProtectedJSValue& a, const ProtectedJSValue& b) { return a.get() != b.get(); } + inline bool operator!=(const ProtectedJSValue& a, const JSValue b) { return a.get() != b; } + template <class T> inline bool operator!=(const ProtectedJSValue& a, const ProtectedPtr<T>& b) { return a.get() != JSValue(b.get()); } + inline bool operator!=(const JSValue a, const ProtectedJSValue& b) { return a != b.get(); } + template <class T> inline bool operator!=(const ProtectedPtr<T>& a, const ProtectedJSValue& b) { return JSValue(a.get()) != b.get(); } + +} // namespace JSC + +#endif // Protect_h diff --git a/Source/JavaScriptCore/runtime/PrototypeFunction.cpp b/Source/JavaScriptCore/runtime/PrototypeFunction.cpp new file mode 100644 index 0000000..3529080 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PrototypeFunction.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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 "PrototypeFunction.h" + +#include "JSGlobalObject.h" +#include <wtf/Assertions.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(PrototypeFunction); + +PrototypeFunction::PrototypeFunction(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction function) + : InternalFunction(&exec->globalData(), globalObject, exec->lexicalGlobalObject()->prototypeFunctionStructure(), name) + , m_function(function) +{ + ASSERT_ARG(function, function); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); +} + +PrototypeFunction::PrototypeFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> prototypeFunctionStructure, int length, const Identifier& name, NativeFunction function) + : InternalFunction(&exec->globalData(), globalObject, prototypeFunctionStructure, name) + , m_function(function) +{ + ASSERT_ARG(function, function); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); +} + +CallType PrototypeFunction::getCallData(CallData& callData) +{ + callData.native.function = m_function; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PrototypeFunction.h b/Source/JavaScriptCore/runtime/PrototypeFunction.h new file mode 100644 index 0000000..6ca2342 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PrototypeFunction.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * 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. + * + */ + +#ifndef PrototypeFunction_h +#define PrototypeFunction_h + +#include "InternalFunction.h" +#include "CallData.h" + +namespace JSC { + + class PrototypeFunction : public InternalFunction { + public: + PrototypeFunction(ExecState*, JSGlobalObject*, int length, const Identifier&, NativeFunction); + PrototypeFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); + + private: + virtual CallType getCallData(CallData&); + + const NativeFunction m_function; + }; + +} // namespace JSC + +#endif // PrototypeFunction_h diff --git a/Source/JavaScriptCore/runtime/PutPropertySlot.h b/Source/JavaScriptCore/runtime/PutPropertySlot.h new file mode 100644 index 0000000..4b0b394 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PutPropertySlot.h @@ -0,0 +1,80 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 PutPropertySlot_h +#define PutPropertySlot_h + +#include <wtf/Assertions.h> + +namespace JSC { + + class JSObject; + class JSFunction; + + class PutPropertySlot { + public: + enum Type { Uncachable, ExistingProperty, NewProperty }; + + PutPropertySlot(bool isStrictMode = false) + : m_type(Uncachable) + , m_base(0) + , m_isStrictMode(isStrictMode) + { + } + + void setExistingProperty(JSObject* base, size_t offset) + { + m_type = ExistingProperty; + m_base = base; + m_offset = offset; + } + + void setNewProperty(JSObject* base, size_t offset) + { + m_type = NewProperty; + m_base = base; + m_offset = offset; + } + + Type type() const { return m_type; } + JSObject* base() const { return m_base; } + + bool isStrictMode() const { return m_isStrictMode; } + bool isCacheable() const { return m_type != Uncachable; } + size_t cachedOffset() const { + ASSERT(isCacheable()); + return m_offset; + } + private: + Type m_type; + JSObject* m_base; + size_t m_offset; + bool m_isStrictMode; + }; + +} // namespace JSC + +#endif // PutPropertySlot_h diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp new file mode 100644 index 0000000..664a7fb --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org) + * Copyright (c) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExp.h" +#include "Lexer.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> + +#include "yarr/RegexJIT.h" +#include "yarr/RegexInterpreter.h" +#include "yarr/RegexPattern.h" + +namespace JSC { + +struct RegExpRepresentation { +#if ENABLE(YARR_JIT) + Yarr::RegexCodeBlock m_regExpJITCode; +#endif + OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; +}; + +inline RegExp::RegExp(JSGlobalData* globalData, const UString& patternString, const UString& flags) + : m_patternString(patternString) + , m_flagBits(0) + , m_constructionError(0) + , m_numSubpatterns(0) +#if ENABLE(REGEXP_TRACING) + , m_rtMatchCallCount(0) + , m_rtMatchFoundCount(0) +#endif + , m_representation(adoptPtr(new RegExpRepresentation)) +{ + // NOTE: The global flag is handled on a case-by-case basis by functions like + // String::match and RegExpObject::match. + if (!flags.isNull()) { + if (flags.find('g') != notFound) + m_flagBits |= Global; + if (flags.find('i') != notFound) + m_flagBits |= IgnoreCase; + if (flags.find('m') != notFound) + m_flagBits |= Multiline; + } + + m_state = compile(globalData); +} + +RegExp::~RegExp() +{ +} + +PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& patternString, const UString& flags) +{ + RefPtr<RegExp> res = adoptRef(new RegExp(globalData, patternString, flags)); +#if ENABLE(REGEXP_TRACING) + globalData->addRegExpToTrace(res); +#endif + return res.release(); +} + +RegExp::RegExpState RegExp::compile(JSGlobalData* globalData) +{ + Yarr::RegexPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) + return ParseError; + + m_numSubpatterns = pattern.m_numSubpatterns; + + RegExpState res = ByteCode; + +#if ENABLE(YARR_JIT) + if (!pattern.m_containsBackreferences && globalData->canUseJIT()) { + Yarr::jitCompileRegex(pattern, globalData, m_representation->m_regExpJITCode); +#if ENABLE(YARR_JIT_DEBUG) + if (!m_representation->m_regExpJITCode.isFallBack()) + res = JITCode; + else + res = ByteCode; +#else + if (!m_representation->m_regExpJITCode.isFallBack()) + return JITCode; +#endif + } +#endif + + m_representation->m_regExpBytecode = Yarr::byteCompileRegex(pattern, &globalData->m_regexAllocator); + + return res; +} + +int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) +{ + if (startOffset < 0) + startOffset = 0; + +#if ENABLE(REGEXP_TRACING) + m_rtMatchCallCount++; +#endif + + if (static_cast<unsigned>(startOffset) > s.length() || s.isNull()) + return -1; + + if (m_state != ParseError) { + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + if (ovector) { + ovector->resize(offsetVectorSize); + offsetVector = ovector->data(); + } else { + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + } + + ASSERT(offsetVector); + // Initialize offsetVector with the return value (index 0) and the + // first subpattern start indicies (even index values) set to -1. + // No need to init the subpattern end indicies. + for (unsigned j = 0, i = 0; i < m_numSubpatterns + 1; j += 2, i++) + offsetVector[j] = -1; + + int result; +#if ENABLE(YARR_JIT) + if (m_state == JITCode) { + result = Yarr::executeRegex(m_representation->m_regExpJITCode, s.characters(), startOffset, s.length(), offsetVector); +#if ENABLE(YARR_JIT_DEBUG) + matchCompareWithInterpreter(s, startOffset, offsetVector, result); +#endif + } else +#endif + result = Yarr::interpretRegex(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), offsetVector); + ASSERT(result >= -1); + +#if ENABLE(REGEXP_TRACING) + if (result != -1) + m_rtMatchFoundCount++; +#endif + + return result; + } + + return -1; +} + + +#if ENABLE(YARR_JIT_DEBUG) +void RegExp::matchCompareWithInterpreter(const UString& s, int startOffset, int* offsetVector, int jitResult) +{ + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + Vector<int, 32> interpreterOvector; + interpreterOvector.resize(offsetVectorSize); + int* interpreterOffsetVector = interpreterOvector.data(); + int interpreterResult = 0; + int differences = 0; + + // Initialize interpreterOffsetVector with the return value (index 0) and the + // first subpattern start indicies (even index values) set to -1. + // No need to init the subpattern end indicies. + for (unsigned j = 0, i = 0; i < m_numSubpatterns + 1; j += 2, i++) + interpreterOffsetVector[j] = -1; + + interpreterResult = Yarr::interpretRegex(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), interpreterOffsetVector); + + if (jitResult != interpreterResult) + differences++; + + for (unsigned j = 2, i = 0; i < m_numSubpatterns; j +=2, i++) + if ((offsetVector[j] != interpreterOffsetVector[j]) + || ((offsetVector[j] >= 0) && (offsetVector[j+1] != interpreterOffsetVector[j+1]))) + differences++; + + if (differences) { + fprintf(stderr, "RegExp Discrepency for /%s/\n string input ", pattern().utf8().data()); + unsigned segmentLen = s.length() - static_cast<unsigned>(startOffset); + + fprintf(stderr, (segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset); + + if (jitResult != interpreterResult) { + fprintf(stderr, " JIT result = %d, blah interpreted result = %d\n", jitResult, interpreterResult); + differences--; + } else { + fprintf(stderr, " Correct result = %d\n", jitResult); + } + + if (differences) { + for (unsigned j = 2, i = 0; i < m_numSubpatterns; j +=2, i++) { + if (offsetVector[j] != interpreterOffsetVector[j]) + fprintf(stderr, " JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j, offsetVector[j], j, interpreterOffsetVector[j]); + if ((offsetVector[j] >= 0) && (offsetVector[j+1] != interpreterOffsetVector[j+1])) + fprintf(stderr, " JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j+1, offsetVector[j+1], j+1, interpreterOffsetVector[j+1]); + } + } + } +} +#endif + +#if ENABLE(REGEXP_TRACING) + void RegExp::printTraceData() + { + char formattedPattern[41]; + char rawPattern[41]; + + strncpy(rawPattern, pattern().utf8().data(), 40); + rawPattern[40]= '\0'; + + int pattLen = strlen(rawPattern); + + snprintf(formattedPattern, 41, (pattLen <= 38) ? "/%.38s/" : "/%.36s...", rawPattern); + +#if ENABLE(YARR_JIT) + Yarr::RegexCodeBlock& codeBlock = m_representation->m_regExpJITCode; + + char jitAddr[20]; + if (m_state == JITCode) + sprintf(jitAddr, "fallback"); + else + sprintf(jitAddr, "0x%014lx", reinterpret_cast<unsigned long int>(codeBlock.getAddr())); +#else + const char* jitAddr = "JIT Off"; +#endif + + printf("%-40.40s %16.16s %10d %10d\n", formattedPattern, jitAddr, m_rtMatchCallCount, m_rtMatchFoundCount); + } +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExp.h b/Source/JavaScriptCore/runtime/RegExp.h new file mode 100644 index 0000000..d99befb --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExp.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExp_h +#define RegExp_h + +#include "UString.h" +#include "ExecutableAllocator.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace JSC { + + struct RegExpRepresentation; + class JSGlobalData; + + class RegExp : public RefCounted<RegExp> { + public: + static PassRefPtr<RegExp> create(JSGlobalData* globalData, const UString& pattern, const UString& flags); + ~RegExp(); + + bool global() const { return m_flagBits & Global; } + bool ignoreCase() const { return m_flagBits & IgnoreCase; } + bool multiline() const { return m_flagBits & Multiline; } + + const UString& pattern() const { return m_patternString; } + + bool isValid() const { return !m_constructionError; } + const char* errorMessage() const { return m_constructionError; } + + int match(const UString&, int startOffset, Vector<int, 32>* ovector = 0); + unsigned numSubpatterns() const { return m_numSubpatterns; } + +#if ENABLE(REGEXP_TRACING) + void printTraceData(); +#endif + + private: + RegExp(JSGlobalData* globalData, const UString& pattern, const UString& flags); + + enum RegExpState { + ParseError, + JITCode, + ByteCode + } m_state; + + RegExpState compile(JSGlobalData*); + +#if ENABLE(YARR_JIT_DEBUG) + void matchCompareWithInterpreter(const UString&, int startOffset, int* offsetVector, int jitResult); +#endif + + enum FlagBits { Global = 1, IgnoreCase = 2, Multiline = 4 }; + UString m_patternString; + int m_flagBits; + const char* m_constructionError; + unsigned m_numSubpatterns; +#if ENABLE(REGEXP_TRACING) + unsigned m_rtMatchCallCount; + unsigned m_rtMatchFoundCount; +#endif + + OwnPtr<RegExpRepresentation> m_representation; + }; + +} // namespace JSC + +#endif // RegExp_h diff --git a/Source/JavaScriptCore/runtime/RegExpCache.cpp b/Source/JavaScriptCore/runtime/RegExpCache.cpp new file mode 100644 index 0000000..d101758 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCache.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "RegExpCache.h" + +namespace JSC { + +PassRefPtr<RegExp> RegExpCache::lookupOrCreate(const UString& patternString, const UString& flags) +{ + if (patternString.length() < maxCacheablePatternLength) { + pair<RegExpCacheMap::iterator, bool> result = m_cacheMap.add(RegExpKey(flags, patternString), 0); + if (!result.second) + return result.first->second; + else + return create(patternString, flags, result.first); + } + return create(patternString, flags, m_cacheMap.end()); +} + +PassRefPtr<RegExp> RegExpCache::create(const UString& patternString, const UString& flags, RegExpCacheMap::iterator iterator) +{ + RefPtr<RegExp> regExp = RegExp::create(m_globalData, patternString, flags); + + if (patternString.length() >= maxCacheablePatternLength) + return regExp; + + RegExpKey key = RegExpKey(flags, patternString); + iterator->first = key; + iterator->second = regExp; + + ++m_nextKeyToEvict; + if (m_nextKeyToEvict == maxCacheableEntries) { + m_nextKeyToEvict = 0; + m_isFull = true; + } + if (m_isFull) + m_cacheMap.remove(RegExpKey(patternKeyArray[m_nextKeyToEvict].flagsValue, patternKeyArray[m_nextKeyToEvict].pattern)); + + patternKeyArray[m_nextKeyToEvict].flagsValue = key.flagsValue; + patternKeyArray[m_nextKeyToEvict].pattern = patternString.impl(); + return regExp; +} + +RegExpCache::RegExpCache(JSGlobalData* globalData) + : m_globalData(globalData) + , m_nextKeyToEvict(-1) + , m_isFull(false) +{ +} + +} diff --git a/Source/JavaScriptCore/runtime/RegExpCache.h b/Source/JavaScriptCore/runtime/RegExpCache.h new file mode 100644 index 0000000..b5b637f --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCache.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "RegExp.h" +#include "RegExpKey.h" +#include "UString.h" +#include <wtf/FixedArray.h> +#include <wtf/HashMap.h> + +#ifndef RegExpCache_h +#define RegExpCache_h + +namespace JSC { + +class RegExpCache { + +typedef HashMap<RegExpKey, RefPtr<RegExp> > RegExpCacheMap; + +public: + PassRefPtr<RegExp> lookupOrCreate(const UString& patternString, const UString& flags); + PassRefPtr<RegExp> create(const UString& patternString, const UString& flags, RegExpCacheMap::iterator iterator); + RegExpCache(JSGlobalData* globalData); + +private: + static const unsigned maxCacheablePatternLength = 256; + +#if PLATFORM(IOS) + // The RegExpCache can currently hold onto multiple Mb of memory; + // as a short-term fix some embedded platforms may wish to reduce the cache size. + static const int maxCacheableEntries = 32; +#else + static const int maxCacheableEntries = 256; +#endif + + FixedArray<RegExpKey, maxCacheableEntries> patternKeyArray; + RegExpCacheMap m_cacheMap; + JSGlobalData* m_globalData; + int m_nextKeyToEvict; + bool m_isFull; +}; + +} // namespace JSC + +#endif // RegExpCache_h diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp new file mode 100644 index 0000000..21ca170 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpConstructor.h" + +#include "ArrayPrototype.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "JSString.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "RegExpMatchesArray.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "RegExp.h" +#include "RegExpCache.h" +#include "UStringConcatenate.h" +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +static JSValue regExpConstructorInput(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorMultiline(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorLastMatch(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorLastParen(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorLeftContext(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorRightContext(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar1(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar2(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar3(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar4(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar5(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar6(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar7(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar8(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar9(ExecState*, JSValue, const Identifier&); + +static void setRegExpConstructorInput(ExecState*, JSObject*, JSValue); +static void setRegExpConstructorMultiline(ExecState*, JSObject*, JSValue); + +} // namespace JSC + +#include "RegExpConstructor.lut.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(RegExpConstructor); + +const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::regExpConstructorTable }; + +/* Source for RegExpConstructor.lut.h +@begin regExpConstructorTable + input regExpConstructorInput None + $_ regExpConstructorInput DontEnum + multiline regExpConstructorMultiline None + $* regExpConstructorMultiline DontEnum + lastMatch regExpConstructorLastMatch DontDelete|ReadOnly + $& regExpConstructorLastMatch DontDelete|ReadOnly|DontEnum + lastParen regExpConstructorLastParen DontDelete|ReadOnly + $+ regExpConstructorLastParen DontDelete|ReadOnly|DontEnum + leftContext regExpConstructorLeftContext DontDelete|ReadOnly + $` regExpConstructorLeftContext DontDelete|ReadOnly|DontEnum + rightContext regExpConstructorRightContext DontDelete|ReadOnly + $' regExpConstructorRightContext DontDelete|ReadOnly|DontEnum + $1 regExpConstructorDollar1 DontDelete|ReadOnly + $2 regExpConstructorDollar2 DontDelete|ReadOnly + $3 regExpConstructorDollar3 DontDelete|ReadOnly + $4 regExpConstructorDollar4 DontDelete|ReadOnly + $5 regExpConstructorDollar5 DontDelete|ReadOnly + $6 regExpConstructorDollar6 DontDelete|ReadOnly + $7 regExpConstructorDollar7 DontDelete|ReadOnly + $8 regExpConstructorDollar8 DontDelete|ReadOnly + $9 regExpConstructorDollar9 DontDelete|ReadOnly +@end +*/ + +RegExpConstructor::RegExpConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "RegExp")) + , d(adoptPtr(new RegExpConstructorPrivate)) +{ + // ECMA 15.10.5.1 RegExp.prototype + putDirectWithoutTransition(exec->propertyNames().prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(2), ReadOnly | DontDelete | DontEnum); +} + +RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate* data) + : JSArray(exec->lexicalGlobalObject()->regExpMatchesArrayStructure(), data->lastNumSubPatterns + 1, CreateInitialized) +{ + RegExpConstructorPrivate* d = new RegExpConstructorPrivate; + d->input = data->lastInput; + d->lastInput = data->lastInput; + d->lastNumSubPatterns = data->lastNumSubPatterns; + unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector + d->lastOvector().resize(offsetVectorSize); + memcpy(d->lastOvector().data(), data->lastOvector().data(), offsetVectorSize * sizeof(int)); + // d->multiline is not needed, and remains uninitialized + + setSubclassData(d); +} + +RegExpMatchesArray::~RegExpMatchesArray() +{ + delete static_cast<RegExpConstructorPrivate*>(subclassData()); +} + +void RegExpMatchesArray::fillArrayInstance(ExecState* exec) +{ + RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(subclassData()); + ASSERT(d); + + unsigned lastNumSubpatterns = d->lastNumSubPatterns; + + for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { + int start = d->lastOvector()[2 * i]; + if (start >= 0) + JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); + else + JSArray::put(exec, i, jsUndefined()); + } + + PutPropertySlot slot; + JSArray::put(exec, exec->propertyNames().index, jsNumber(d->lastOvector()[0]), slot); + JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->input), slot); + + delete d; + setSubclassData(0); +} + +JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const +{ + return new (exec) RegExpMatchesArray(exec, d.get()); +} + +JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const +{ + if (!d->lastOvector().isEmpty() && i <= d->lastNumSubPatterns) { + int start = d->lastOvector()[2 * i]; + if (start >= 0) + return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); + } + return jsEmptyString(exec); +} + +JSValue RegExpConstructor::getLastParen(ExecState* exec) const +{ + unsigned i = d->lastNumSubPatterns; + if (i > 0) { + ASSERT(!d->lastOvector().isEmpty()); + int start = d->lastOvector()[2 * i]; + if (start >= 0) + return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); + } + return jsEmptyString(exec); +} + +JSValue RegExpConstructor::getLeftContext(ExecState* exec) const +{ + if (!d->lastOvector().isEmpty()) + return jsSubstring(exec, d->lastInput, 0, d->lastOvector()[0]); + return jsEmptyString(exec); +} + +JSValue RegExpConstructor::getRightContext(ExecState* exec) const +{ + if (!d->lastOvector().isEmpty()) + return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.length() - d->lastOvector()[1]); + return jsEmptyString(exec); +} + +bool RegExpConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + 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, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 1); +} + +JSValue regExpConstructorDollar2(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 2); +} + +JSValue regExpConstructorDollar3(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 3); +} + +JSValue regExpConstructorDollar4(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 4); +} + +JSValue regExpConstructorDollar5(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 5); +} + +JSValue regExpConstructorDollar6(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 6); +} + +JSValue regExpConstructorDollar7(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 7); +} + +JSValue regExpConstructorDollar8(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 8); +} + +JSValue regExpConstructorDollar9(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 9); +} + +JSValue regExpConstructorInput(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return jsString(exec, asRegExpConstructor(slotBase)->input()); +} + +JSValue regExpConstructorMultiline(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpConstructor(slotBase)->multiline()); +} + +JSValue regExpConstructorLastMatch(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getBackref(exec, 0); +} + +JSValue regExpConstructorLastParen(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getLastParen(exec); +} + +JSValue regExpConstructorLeftContext(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getLeftContext(exec); +} + +JSValue regExpConstructorRightContext(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return asRegExpConstructor(slotBase)->getRightContext(exec); +} + +void RegExpConstructor::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), this, slot); +} + +void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValue value) +{ + asRegExpConstructor(baseObject)->setInput(value.toString(exec)); +} + +void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValue value) +{ + asRegExpConstructor(baseObject)->setMultiline(value.toBoolean(exec)); +} + +// ECMA 15.10.4 +JSObject* constructRegExp(ExecState* exec, const ArgList& args) +{ + JSValue arg0 = args.at(0); + JSValue arg1 = args.at(1); + + if (arg0.inherits(&RegExpObject::info)) { + if (!arg1.isUndefined()) + return throwError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another.")); + return asObject(arg0); + } + + UString pattern = arg0.isUndefined() ? UString("") : arg0.toString(exec); + UString flags = arg1.isUndefined() ? UString("") : arg1.toString(exec); + + RefPtr<RegExp> regExp = exec->globalData().regExpCache()->lookupOrCreate(pattern, flags); + if (!regExp->isValid()) + return throwError(exec, createSyntaxError(exec, makeUString("Invalid regular expression: ", regExp->errorMessage()))); + return new (exec) RegExpObject(exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); +} + +static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, args)); +} + +ConstructType RegExpConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithRegExpConstructor; + return ConstructTypeHost; +} + +// ECMA 15.10.3 +static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, args)); +} + +CallType RegExpConstructor::getCallData(CallData& callData) +{ + callData.native.function = callRegExpConstructor; + return CallTypeHost; +} + +void RegExpConstructor::setInput(const UString& input) +{ + d->input = input; +} + +const UString& RegExpConstructor::input() const +{ + // Can detect a distinct initial state that is invisible to JavaScript, by checking for null + // state (since jsString turns null strings to empty strings). + return d->input; +} + +void RegExpConstructor::setMultiline(bool multiline) +{ + d->multiline = multiline; +} + +bool RegExpConstructor::multiline() const +{ + return d->multiline; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.h b/Source/JavaScriptCore/runtime/RegExpConstructor.h new file mode 100644 index 0000000..58abde5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpConstructor_h +#define RegExpConstructor_h + +#include "InternalFunction.h" +#include "RegExp.h" +#include <wtf/OwnPtr.h> + +namespace JSC { + + class RegExp; + class RegExpPrototype; + struct RegExpConstructorPrivate; + + struct RegExpConstructorPrivate : FastAllocBase { + // Global search cache / settings + RegExpConstructorPrivate() + : lastNumSubPatterns(0) + , multiline(false) + , lastOvectorIndex(0) + { + } + + const Vector<int, 32>& lastOvector() const { return ovector[lastOvectorIndex]; } + Vector<int, 32>& lastOvector() { return ovector[lastOvectorIndex]; } + Vector<int, 32>& tempOvector() { return ovector[lastOvectorIndex ? 0 : 1]; } + void changeLastOvector() { lastOvectorIndex = lastOvectorIndex ? 0 : 1; } + + UString input; + UString lastInput; + Vector<int, 32> ovector[2]; + unsigned lastNumSubPatterns : 30; + bool multiline : 1; + unsigned lastOvectorIndex : 1; + }; + + class RegExpConstructor : public InternalFunction { + public: + RegExpConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, RegExpPrototype*); + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + 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; + + void performMatch(RegExp*, const UString&, int startOffset, int& position, int& length, int** ovector = 0); + JSObject* arrayOfMatches(ExecState*) const; + + void setInput(const UString&); + const UString& input() const; + + void setMultiline(bool); + bool multiline() const; + + JSValue getBackref(ExecState*, unsigned) const; + JSValue getLastParen(ExecState*) const; + JSValue getLeftContext(ExecState*) const; + JSValue getRightContext(ExecState*) const; + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | InternalFunction::StructureFlags; + + private: + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + + virtual const ClassInfo* classInfo() const { return &info; } + + OwnPtr<RegExpConstructorPrivate> d; + }; + + RegExpConstructor* asRegExpConstructor(JSValue); + + JSObject* constructRegExp(ExecState*, const ArgList&); + + inline RegExpConstructor* asRegExpConstructor(JSValue value) + { + ASSERT(asObject(value)->inherits(&RegExpConstructor::info)); + return static_cast<RegExpConstructor*>(asObject(value)); + } + + /* + To facilitate result caching, exec(), test(), match(), search(), and replace() dipatch regular + expression matching through the performMatch function. We use cached results to calculate, + e.g., RegExp.lastMatch and RegExp.leftParen. + */ + ALWAYS_INLINE void RegExpConstructor::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) + { + position = r->match(s, startOffset, &d->tempOvector()); + + if (ovector) + *ovector = d->tempOvector().data(); + + if (position != -1) { + ASSERT(!d->tempOvector().isEmpty()); + + length = d->tempOvector()[1] - d->tempOvector()[0]; + + d->input = s; + d->lastInput = s; + d->changeLastOvector(); + d->lastNumSubPatterns = r->numSubpatterns(); + } + } + +} // namespace JSC + +#endif // RegExpConstructor_h diff --git a/Source/JavaScriptCore/runtime/RegExpKey.h b/Source/JavaScriptCore/runtime/RegExpKey.h new file mode 100644 index 0000000..cd1368d --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpKey.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "UString.h" +#include <wtf/text/StringHash.h> + +#ifndef RegExpKey_h +#define RegExpKey_h + +namespace JSC { + +struct RegExpKey { + int flagsValue; + RefPtr<StringImpl> pattern; + + RegExpKey() + : flagsValue(0) + { + } + + RegExpKey(int flags) + : flagsValue(flags) + { + } + + RegExpKey(int flags, const UString& pattern) + : flagsValue(flags) + , pattern(pattern.impl()) + { + } + + RegExpKey(int flags, const PassRefPtr<StringImpl> pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + RegExpKey(int flags, const RefPtr<StringImpl>& pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + RegExpKey(const UString& flags, const UString& pattern) + : pattern(pattern.impl()) + { + flagsValue = getFlagsValue(flags); + } + + int getFlagsValue(const UString flags) + { + flagsValue = 0; + if (flags.find('g') != notFound) + flagsValue += 4; + if (flags.find('i') != notFound) + flagsValue += 2; + if (flags.find('m') != notFound) + flagsValue += 1; + return flagsValue; + } +}; + +inline bool operator==(const RegExpKey& a, const RegExpKey& b) +{ + if (a.flagsValue != b.flagsValue) + return false; + if (!a.pattern) + return !b.pattern; + if (!b.pattern) + return false; + return equal(a.pattern.get(), b.pattern.get()); +} + +} // namespace JSC + +namespace WTF { +template<typename T> struct DefaultHash; +template<typename T> struct RegExpHash; + +template<> struct RegExpHash<JSC::RegExpKey> { + static unsigned hash(const JSC::RegExpKey& key) { return key.pattern->hash(); } + static bool equal(const JSC::RegExpKey& a, const JSC::RegExpKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +template<> struct DefaultHash<JSC::RegExpKey> { + typedef RegExpHash<JSC::RegExpKey> Hash; +}; + +template<> struct HashTraits<JSC::RegExpKey> : GenericHashTraits<JSC::RegExpKey> { + static void constructDeletedValue(JSC::RegExpKey& slot) { slot.flagsValue = -1; } + static bool isDeletedValue(const JSC::RegExpKey& value) { return value.flagsValue == -1; } +}; +} // namespace WTF + +#endif // RegExpKey_h diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h new file mode 100644 index 0000000..b823621 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpMatchesArray_h +#define RegExpMatchesArray_h + +#include "JSArray.h" + +namespace JSC { + + class RegExpMatchesArray : public JSArray { + public: + RegExpMatchesArray(ExecState*, RegExpConstructorPrivate*); + virtual ~RegExpMatchesArray(); + + private: + virtual bool getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) + { + if (subclassData()) + fillArrayInstance(exec); + return JSArray::getOwnPropertySlot(exec, propertyName, slot); + } + + virtual bool getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) + { + if (subclassData()) + fillArrayInstance(exec); + return JSArray::getOwnPropertySlot(exec, propertyName, slot); + } + + virtual bool getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + if (subclassData()) + fillArrayInstance(exec); + return JSArray::getOwnPropertyDescriptor(exec, propertyName, descriptor); + } + + virtual void put(ExecState* exec, const Identifier& propertyName, JSValue v, PutPropertySlot& slot) + { + if (subclassData()) + fillArrayInstance(exec); + JSArray::put(exec, propertyName, v, slot); + } + + virtual void put(ExecState* exec, unsigned propertyName, JSValue v) + { + if (subclassData()) + fillArrayInstance(exec); + JSArray::put(exec, propertyName, v); + } + + virtual bool deleteProperty(ExecState* exec, const Identifier& propertyName) + { + if (subclassData()) + fillArrayInstance(exec); + return JSArray::deleteProperty(exec, propertyName); + } + + virtual bool deleteProperty(ExecState* exec, unsigned propertyName) + { + if (subclassData()) + fillArrayInstance(exec); + return JSArray::deleteProperty(exec, propertyName); + } + + virtual void getOwnPropertyNames(ExecState* exec, PropertyNameArray& arr, EnumerationMode mode = ExcludeDontEnumProperties) + { + if (subclassData()) + fillArrayInstance(exec); + JSArray::getOwnPropertyNames(exec, arr, mode); + } + + void fillArrayInstance(ExecState*); +}; + +} + +#endif // RegExpMatchesArray_h diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp new file mode 100644 index 0000000..7fda5b1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpObject.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Lookup.h" +#include "RegExpConstructor.h" +#include "RegExpPrototype.h" +#include "UStringConcatenate.h" +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +static JSValue regExpObjectGlobal(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectMultiline(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectSource(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectLastIndex(ExecState*, JSValue, const Identifier&); +static void setRegExpObjectLastIndex(ExecState*, JSObject*, JSValue); + +} // namespace JSC + +#include "RegExpObject.lut.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(RegExpObject); + +const ClassInfo RegExpObject::info = { "RegExp", 0, 0, ExecState::regExpTable }; + +/* Source for RegExpObject.lut.h +@begin regExpTable + global regExpObjectGlobal DontDelete|ReadOnly|DontEnum + ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum + multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum + source regExpObjectSource DontDelete|ReadOnly|DontEnum + lastIndex regExpObjectLastIndex DontDelete|DontEnum +@end +*/ + +RegExpObject::RegExpObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<RegExp> regExp) + : JSObjectWithGlobalObject(globalObject, structure) + , d(adoptPtr(new RegExpObjectData(regExp, 0))) +{ +} + +RegExpObject::~RegExpObject() +{ +} + +bool RegExpObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + 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*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpObject(slotBase)->regExp()->global()); +} + +JSValue regExpObjectIgnoreCase(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase()); +} + +JSValue regExpObjectMultiline(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpObject(slotBase)->regExp()->multiline()); +} + +JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, const Identifier&) +{ + return jsString(exec, asRegExpObject(slotBase)->regExp()->pattern()); +} + +JSValue regExpObjectLastIndex(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsNumber(asRegExpObject(slotBase)->lastIndex()); +} + +void RegExpObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), this, slot); +} + +void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue value) +{ + asRegExpObject(baseObject)->setLastIndex(value.toInteger(exec)); +} + +JSValue RegExpObject::test(ExecState* exec) +{ + return jsBoolean(match(exec)); +} + +JSValue RegExpObject::exec(ExecState* exec) +{ + if (match(exec)) + return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec); + return jsNull(); +} + +static EncodedJSValue JSC_HOST_CALL callRegExpObject(ExecState* exec) +{ + return JSValue::encode(asRegExpObject(exec->callee())->exec(exec)); +} + +CallType RegExpObject::getCallData(CallData& callData) +{ + callData.native.function = callRegExpObject; + return CallTypeHost; +} + +// Shared implementation used by test and exec. +bool RegExpObject::match(ExecState* exec) +{ + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + + UString input = !exec->argumentCount() ? regExpConstructor->input() : exec->argument(0).toString(exec); + if (input.isNull()) { + throwError(exec, createError(exec, makeUString("No input to ", toString(exec), "."))); + return false; + } + + if (!regExp()->global()) { + int position; + int length; + regExpConstructor->performMatch(d->regExp.get(), input, 0, position, length); + return position >= 0; + } + + if (d->lastIndex < 0 || d->lastIndex > input.length()) { + d->lastIndex = 0; + return false; + } + + int position; + int length = 0; + regExpConstructor->performMatch(d->regExp.get(), input, static_cast<int>(d->lastIndex), position, length); + if (position < 0) { + d->lastIndex = 0; + return false; + } + + d->lastIndex = position + length; + return true; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpObject.h b/Source/JavaScriptCore/runtime/RegExpObject.h new file mode 100644 index 0000000..19de929 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpObject_h +#define RegExpObject_h + +#include "JSObjectWithGlobalObject.h" +#include "RegExp.h" + +namespace JSC { + + class RegExpObject : public JSObjectWithGlobalObject { + public: + RegExpObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure>, NonNullPassRefPtr<RegExp>); + virtual ~RegExpObject(); + + void setRegExp(PassRefPtr<RegExp> r) { d->regExp = r; } + RegExp* regExp() const { return d->regExp.get(); } + + void setLastIndex(double lastIndex) { d->lastIndex = lastIndex; } + double lastIndex() const { return d->lastIndex; } + + JSValue test(ExecState*); + JSValue exec(ExecState*); + + 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; } + static JS_EXPORTDATA const ClassInfo info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObjectWithGlobalObject::StructureFlags; + + private: + bool match(ExecState*); + + virtual CallType getCallData(CallData&); + + struct RegExpObjectData : FastAllocBase { + RegExpObjectData(NonNullPassRefPtr<RegExp> regExp, double lastIndex) + : regExp(regExp) + , lastIndex(lastIndex) + { + } + + RefPtr<RegExp> regExp; + double lastIndex; + }; + + OwnPtr<RegExpObjectData> d; + }; + + RegExpObject* asRegExpObject(JSValue); + + inline RegExpObject* asRegExpObject(JSValue value) + { + ASSERT(asObject(value)->inherits(&RegExpObject::info)); + return static_cast<RegExpObject*>(asObject(value)); + } + +} // namespace JSC + +#endif // RegExpObject_h diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp new file mode 100644 index 0000000..0a4c8bf --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpPrototype.h" + +#include "ArrayPrototype.h" +#include "Error.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "JSObject.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "JSValue.h" +#include "ObjectPrototype.h" +#include "PrototypeFunction.h" +#include "RegExpObject.h" +#include "RegExp.h" +#include "RegExpCache.h" +#include "UStringConcatenate.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(RegExpPrototype); + +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*); + +// ECMA 15.10.5 + +RegExpPrototype::RegExpPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) + : RegExpObject(globalObject, structure, RegExp::create(&exec->globalData(), "", "")) +{ + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().compile, regExpProtoFuncCompile), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().exec, regExpProtoFuncExec), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().test, regExpProtoFuncTest), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, regExpProtoFuncToString), DontEnum); +} + +// ------------------------------ Functions --------------------------- + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::info)) + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->test(exec)); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::info)) + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->exec(exec)); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::info)) + return throwVMTypeError(exec); + + RefPtr<RegExp> regExp; + JSValue arg0 = exec->argument(0); + JSValue arg1 = exec->argument(1); + + if (arg0.inherits(&RegExpObject::info)) { + if (!arg1.isUndefined()) + return throwVMError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another.")); + regExp = asRegExpObject(arg0)->regExp(); + } else { + UString pattern = !exec->argumentCount() ? UString("") : arg0.toString(exec); + UString flags = arg1.isUndefined() ? UString("") : arg1.toString(exec); + regExp = exec->globalData().regExpCache()->lookupOrCreate(pattern, flags); + } + + if (!regExp->isValid()) + return throwVMError(exec, createSyntaxError(exec, makeUString("Invalid regular expression: ", regExp->errorMessage()))); + + asRegExpObject(thisValue)->setRegExp(regExp.release()); + asRegExpObject(thisValue)->setLastIndex(0); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::info)) { + if (thisValue.inherits(&RegExpPrototype::info)) + return JSValue::encode(jsNontrivialString(exec, "//")); + return throwVMTypeError(exec); + } + + char postfix[5] = { '/', 0, 0, 0, 0 }; + int index = 1; + if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().global).toBoolean(exec)) + postfix[index++] = 'g'; + if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec)) + postfix[index++] = 'i'; + if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().multiline).toBoolean(exec)) + postfix[index] = 'm'; + UString source = asRegExpObject(thisValue)->get(exec, exec->propertyNames().source).toString(exec); + // If source is empty, use "/(?:)/" to avoid colliding with comment syntax + return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.h b/Source/JavaScriptCore/runtime/RegExpPrototype.h new file mode 100644 index 0000000..eb4ae00 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpPrototype_h +#define RegExpPrototype_h + +#include "RegExpObject.h" +#include "JSObject.h" + +namespace JSC { + + class RegExpPrototype : public RegExpObject { + public: + RegExpPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + }; + +} // namespace JSC + +#endif // RegExpPrototype_h diff --git a/Source/JavaScriptCore/runtime/RopeImpl.cpp b/Source/JavaScriptCore/runtime/RopeImpl.cpp new file mode 100644 index 0000000..09c24a9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RopeImpl.cpp @@ -0,0 +1,62 @@ +/* + * 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 "RopeImpl.h" + +namespace JSC { + +void RopeImpl::derefFibersNonRecursive(Vector<RopeImpl*, 32>& workQueue) +{ + unsigned fiberCount = this->fiberCount(); + for (unsigned i = 0; i < fiberCount; ++i) { + Fiber& fiber = m_fibers[i]; + if (isRope(fiber)) { + RopeImpl* nextRope = static_cast<RopeImpl*>(fiber); + if (nextRope->hasOneRef()) + workQueue.append(nextRope); + else + nextRope->deref(); + } else + static_cast<StringImpl*>(fiber)->deref(); + } +} + +void RopeImpl::destructNonRecursive() +{ + Vector<RopeImpl*, 32> workQueue; + + derefFibersNonRecursive(workQueue); + delete this; + + while (!workQueue.isEmpty()) { + RopeImpl* rope = workQueue.last(); + workQueue.removeLast(); + rope->derefFibersNonRecursive(workQueue); + delete rope; + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RopeImpl.h b/Source/JavaScriptCore/runtime/RopeImpl.h new file mode 100644 index 0000000..dfacbf5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RopeImpl.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009, 2010 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 RopeImpl_h +#define RopeImpl_h + +#include <wtf/text/StringImpl.h> + +namespace JSC { + +class RopeImpl : public StringImplBase { +public: + // A RopeImpl is composed from a set of smaller strings called Fibers. + // Each Fiber in a rope is either StringImpl or another RopeImpl. + typedef StringImplBase* Fiber; + + // Creates a RopeImpl comprising of 'fiberCount' Fibers. + // The RopeImpl is constructed in an uninitialized state - initialize must be called for each Fiber in the RopeImpl. + static PassRefPtr<RopeImpl> tryCreateUninitialized(unsigned fiberCount) + { + void* allocation; + if (tryFastMalloc(sizeof(RopeImpl) + (fiberCount - 1) * sizeof(Fiber)).getValue(allocation)) + return adoptRef(new (allocation) RopeImpl(fiberCount)); + return 0; + } + + static bool isRope(Fiber fiber) + { + return !fiber->isStringImpl(); + } + + static void deref(Fiber fiber) + { + if (isRope(fiber)) + static_cast<RopeImpl*>(fiber)->deref(); + else + static_cast<StringImpl*>(fiber)->deref(); + } + + void initializeFiber(unsigned &index, Fiber fiber) + { + m_fibers[index++] = fiber; + fiber->ref(); + m_length += fiber->length(); + } + + unsigned fiberCount() { return m_size; } + Fiber* fibers() { return m_fibers; } + + ALWAYS_INLINE void deref() + { + m_refCountAndFlags -= s_refCountIncrement; + if (!(m_refCountAndFlags & s_refCountMask)) + destructNonRecursive(); + } + +private: + RopeImpl(unsigned fiberCount) + : StringImplBase(ConstructNonStringImpl) + , m_size(fiberCount) + { + } + + void destructNonRecursive(); + void derefFibersNonRecursive(Vector<RopeImpl*, 32>& workQueue); + + bool hasOneRef() { return (m_refCountAndFlags & s_refCountMask) == s_refCountIncrement; } + + unsigned m_size; + Fiber m_fibers[1]; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/ScopeChain.cpp b/Source/JavaScriptCore/runtime/ScopeChain.cpp new file mode 100644 index 0000000..54c5082 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeChain.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. + * + * 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 "ScopeChain.h" + +#include "JSActivation.h" +#include "JSGlobalObject.h" +#include "JSObject.h" +#include "PropertyNameArray.h" +#include <stdio.h> + +namespace JSC { + +#ifndef NDEBUG + +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::const_iterator propEnd = propertyNames.end(); + + fprintf(stderr, "----- [scope %p] -----\n", o); + for (PropertyNameArray::const_iterator propIter = propertyNames.begin(); propIter != propEnd; propIter++) { + Identifier name = *propIter; + fprintf(stderr, "%s, ", name.ustring().utf8().data()); + } + fprintf(stderr, "\n"); + } +} + +#endif + +int ScopeChain::localDepth() const +{ + int scopeDepth = 0; + ScopeChainIterator iter = this->begin(); + ScopeChainIterator end = this->end(); + while (!(*iter)->inherits(&JSActivation::info)) { + ++iter; + if (iter == end) + break; + ++scopeDepth; + } + return scopeDepth; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ScopeChain.h b/Source/JavaScriptCore/runtime/ScopeChain.h new file mode 100644 index 0000000..0b15b67 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeChain.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2003, 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 + * 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. + * + */ + +#ifndef ScopeChain_h +#define ScopeChain_h + +#include "FastAllocBase.h" + +namespace JSC { + + class JSGlobalData; + class JSGlobalObject; + class JSObject; + class MarkStack; + class ScopeChainIterator; + + class ScopeChainNode : public FastAllocBase { + public: + 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 + // to deleted but still "valid" ScopeChainNodes we now deliberately clobber the + // contents in debug builds. + ~ScopeChainNode() + { + next = 0; + object = 0; + globalData = 0; + globalObject = 0; + globalThis = 0; + } +#endif + + ScopeChainNode* next; + JSObject* object; + JSGlobalData* globalData; + JSGlobalObject* globalObject; + JSObject* globalThis; + int refCount; + + void deref() { ASSERT(refCount); if (--refCount == 0) { release();} } + void ref() { ASSERT(refCount); ++refCount; } + void release(); + + // Before calling "push" on a bare ScopeChainNode, a client should + // logically "copy" the node. Later, the client can "deref" the head + // of its chain of ScopeChainNodes to reclaim all the nodes it added + // after the logical copy, leaving nodes added before the logical copy + // (nodes shared with other clients) untouched. + ScopeChainNode* copy() + { + ref(); + return this; + } + + ScopeChainNode* push(JSObject*); + ScopeChainNode* pop(); + + ScopeChainIterator begin() const; + ScopeChainIterator end() const; + +#ifndef NDEBUG + void print() const; +#endif + }; + + inline ScopeChainNode* ScopeChainNode::push(JSObject* o) + { + ASSERT(o); + return new ScopeChainNode(this, o, globalData, globalObject, globalThis); + } + + inline ScopeChainNode* ScopeChainNode::pop() + { + ASSERT(next); + ScopeChainNode* result = next; + + if (--refCount != 0) + ++result->refCount; + else + delete this; + + return result; + } + + inline void ScopeChainNode::release() + { + // This function is only called by deref(), + // Deref ensures these conditions are true. + ASSERT(refCount == 0); + ScopeChainNode* n = this; + do { + ScopeChainNode* next = n->next; + delete n; + n = next; + } while (n && --n->refCount == 0); + } + + class ScopeChainIterator { + public: + ScopeChainIterator(const ScopeChainNode* node) + : m_node(node) + { + } + + JSObject* const & operator*() const { return m_node->object; } + JSObject* const * operator->() const { return &(operator*()); } + + ScopeChainIterator& operator++() { m_node = m_node->next; return *this; } + + // postfix ++ intentionally omitted + + bool operator==(const ScopeChainIterator& other) const { return m_node == other.m_node; } + bool operator!=(const ScopeChainIterator& other) const { return m_node != other.m_node; } + + private: + const ScopeChainNode* m_node; + }; + + inline ScopeChainIterator ScopeChainNode::begin() const + { + return ScopeChainIterator(this); + } + + inline ScopeChainIterator ScopeChainNode::end() const + { + return ScopeChainIterator(0); + } + + class NoScopeChain {}; + + class ScopeChain { + friend class JIT; + public: + ScopeChain(NoScopeChain) + : m_node(0) + { + } + + ScopeChain(JSObject* o, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) + : m_node(new ScopeChainNode(0, o, globalData, globalObject, globalThis)) + { + } + + ScopeChain(const ScopeChain& c) + : m_node(c.m_node->copy()) + { + } + + ScopeChain& operator=(const ScopeChain& c); + + explicit ScopeChain(ScopeChainNode* node) + : m_node(node->copy()) + { + } + + ~ScopeChain() + { + if (m_node) + m_node->deref(); +#ifndef NDEBUG + m_node = 0; +#endif + } + + void swap(ScopeChain&); + + ScopeChainNode* node() const { return m_node; } + + JSObject* top() const { return m_node->object; } + + ScopeChainIterator begin() const { return m_node->begin(); } + ScopeChainIterator end() const { return m_node->end(); } + + void push(JSObject* o) { m_node = m_node->push(o); } + + void pop() { m_node = m_node->pop(); } + void clear() { m_node->deref(); m_node = 0; } + + JSGlobalObject* globalObject() const { return m_node->globalObject; } + + void markAggregate(MarkStack&) const; + + // Caution: this should only be used if the codeblock this is being used + // with needs a full scope chain, otherwise this returns the depth of + // the preceeding call frame + // + // Returns the depth of the current call frame's scope chain + int localDepth() const; + +#ifndef NDEBUG + void print() const { m_node->print(); } +#endif + + private: + ScopeChainNode* m_node; + }; + + inline void ScopeChain::swap(ScopeChain& o) + { + ScopeChainNode* tmp = m_node; + m_node = o.m_node; + o.m_node = tmp; + } + + inline ScopeChain& ScopeChain::operator=(const ScopeChain& c) + { + ScopeChain tmp(c); + swap(tmp); + return *this; + } + +} // namespace JSC + +#endif // ScopeChain_h diff --git a/Source/JavaScriptCore/runtime/ScopeChainMark.h b/Source/JavaScriptCore/runtime/ScopeChainMark.h new file mode 100644 index 0000000..984d101 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeChainMark.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2003, 2006, 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 + * 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. + * + */ + +#ifndef ScopeChainMark_h +#define ScopeChainMark_h + +#include "ScopeChain.h" + +namespace JSC { + + inline void ScopeChain::markAggregate(MarkStack& markStack) const + { + for (ScopeChainNode* n = m_node; n; n = n->next) + markStack.append(n->object); + } + +} // namespace JSC + +#endif // ScopeChainMark_h diff --git a/Source/JavaScriptCore/runtime/SmallStrings.cpp b/Source/JavaScriptCore/runtime/SmallStrings.cpp new file mode 100644 index 0000000..f358727 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SmallStrings.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008, 2010 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 "SmallStrings.h" + +#include "JSGlobalObject.h" +#include "JSString.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +static const unsigned numCharactersToStore = 0x100; + +static inline bool isMarked(JSString* string) +{ + return string && Heap::isCellMarked(string); +} + +class SmallStringsStorage : public Noncopyable { +public: + SmallStringsStorage(); + + StringImpl* rep(unsigned char character) { return m_reps[character].get(); } + +private: + RefPtr<StringImpl> m_reps[numCharactersToStore]; +}; + +SmallStringsStorage::SmallStringsStorage() +{ + UChar* characterBuffer = 0; + RefPtr<StringImpl> baseString = StringImpl::createUninitialized(numCharactersToStore, characterBuffer); + for (unsigned i = 0; i < numCharactersToStore; ++i) { + characterBuffer[i] = i; + m_reps[i] = StringImpl::create(baseString, i, 1); + } +} + +SmallStrings::SmallStrings() +{ + COMPILE_ASSERT(numCharactersToStore == sizeof(m_singleCharacterStrings) / sizeof(m_singleCharacterStrings[0]), IsNumCharactersConstInSyncWithClassUsage); + clear(); +} + +SmallStrings::~SmallStrings() +{ +} + +void SmallStrings::markChildren(MarkStack& markStack) +{ + /* + Our hypothesis is that small strings are very common. So, we cache them + to avoid GC churn. However, in cases where this hypothesis turns out to + be false -- including the degenerate case where all JavaScript execution + has terminated -- we don't want to waste memory. + + To test our hypothesis, we check if any small string has been marked. If + so, it's probably reasonable to mark the rest. If not, we clear the cache. + */ + + bool isAnyStringMarked = isMarked(m_emptyString); + for (unsigned i = 0; i < numCharactersToStore && !isAnyStringMarked; ++i) + isAnyStringMarked = isMarked(m_singleCharacterStrings[i]); + + if (!isAnyStringMarked) { + clear(); + return; + } + + if (m_emptyString) + markStack.append(m_emptyString); + for (unsigned i = 0; i < numCharactersToStore; ++i) { + if (m_singleCharacterStrings[i]) + markStack.append(m_singleCharacterStrings[i]); + } +} + +void SmallStrings::clear() +{ + m_emptyString = 0; + for (unsigned i = 0; i < numCharactersToStore; ++i) + m_singleCharacterStrings[i] = 0; +} + +unsigned SmallStrings::count() const +{ + unsigned count = 0; + if (m_emptyString) + ++count; + for (unsigned i = 0; i < numCharactersToStore; ++i) { + if (m_singleCharacterStrings[i]) + ++count; + } + return count; +} + +void SmallStrings::createEmptyString(JSGlobalData* globalData) +{ + ASSERT(!m_emptyString); + m_emptyString = new (globalData) JSString(globalData, "", JSString::HasOtherOwner); +} + +void SmallStrings::createSingleCharacterString(JSGlobalData* globalData, unsigned char character) +{ + if (!m_storage) + m_storage = adoptPtr(new SmallStringsStorage); + ASSERT(!m_singleCharacterStrings[character]); + m_singleCharacterStrings[character] = new (globalData) JSString(globalData, PassRefPtr<StringImpl>(m_storage->rep(character)), JSString::HasOtherOwner); +} + +StringImpl* SmallStrings::singleCharacterStringRep(unsigned char character) +{ + if (!m_storage) + m_storage = adoptPtr(new SmallStringsStorage); + return m_storage->rep(character); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SmallStrings.h b/Source/JavaScriptCore/runtime/SmallStrings.h new file mode 100644 index 0000000..d1ebfb1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SmallStrings.h @@ -0,0 +1,78 @@ +/* + * 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 + * 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 SmallStrings_h +#define SmallStrings_h + +#include "UString.h" +#include <wtf/FixedArray.h> +#include <wtf/OwnPtr.h> + +namespace JSC { + + class JSGlobalData; + class JSString; + class MarkStack; + class SmallStringsStorage; + + class SmallStrings : public Noncopyable { + public: + SmallStrings(); + ~SmallStrings(); + + JSString* emptyString(JSGlobalData* globalData) + { + if (!m_emptyString) + createEmptyString(globalData); + return m_emptyString; + } + JSString* singleCharacterString(JSGlobalData* globalData, unsigned char character) + { + if (!m_singleCharacterStrings[character]) + createSingleCharacterString(globalData, character); + return m_singleCharacterStrings[character]; + } + + StringImpl* singleCharacterStringRep(unsigned char character); + + void markChildren(MarkStack&); + void clear(); + + unsigned count() const; +#if ENABLE(JIT) + JSString** singleCharacterStrings() { return m_singleCharacterStrings.data(); } +#endif + private: + void createEmptyString(JSGlobalData*); + void createSingleCharacterString(JSGlobalData*, unsigned char); + + JSString* m_emptyString; + FixedArray<JSString*, 0x100> m_singleCharacterStrings; + OwnPtr<SmallStringsStorage> m_storage; + }; + +} // namespace JSC + +#endif // SmallStrings_h diff --git a/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp b/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp new file mode 100644 index 0000000..5bb013b --- /dev/null +++ b/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "StrictEvalActivation.h" + +namespace JSC { + +StrictEvalActivation::StrictEvalActivation(ExecState* exec) + : JSObject(exec->globalData().strictEvalActivationStructure) +{ +} + +bool StrictEvalActivation::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +JSObject* StrictEvalActivation::toThisObject(ExecState* exec) const +{ + return exec->globalThisValue(); +} + +JSValue StrictEvalActivation::toStrictThisObject(ExecState*) const +{ + return jsNull(); +} + +} diff --git a/Source/JavaScriptCore/runtime/StrictEvalActivation.h b/Source/JavaScriptCore/runtime/StrictEvalActivation.h new file mode 100644 index 0000000..1385eec --- /dev/null +++ b/Source/JavaScriptCore/runtime/StrictEvalActivation.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 StrictEvalActivation_h +#define StrictEvalActivation_h + +#include "JSObject.h" + +namespace JSC { + +class StrictEvalActivation : public JSObject { +public: + StrictEvalActivation(ExecState*); + virtual bool deleteProperty(ExecState*, const Identifier&); + virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; +}; + +} // namespace JSC + +#endif // StrictEvalActivation_h diff --git a/Source/JavaScriptCore/runtime/StringConstructor.cpp b/Source/JavaScriptCore/runtime/StringConstructor.cpp new file mode 100644 index 0000000..101650c --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringConstructor.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringConstructor.h" + +#include "Executable.h" +#include "JITCode.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "PrototypeFunction.h" +#include "StringPrototype.h" + +namespace JSC { + +static NEVER_INLINE JSValue stringFromCharCodeSlowCase(ExecState* exec) +{ + unsigned length = exec->argumentCount(); + UChar* buf; + PassRefPtr<StringImpl> impl = StringImpl::createUninitialized(length, buf); + for (unsigned i = 0; i < length; ++i) + buf[i] = static_cast<UChar>(exec->argument(i).toUInt32(exec)); + return jsString(exec, impl); +} + +static EncodedJSValue JSC_HOST_CALL stringFromCharCode(ExecState* exec) +{ + if (LIKELY(exec->argumentCount() == 1)) + return JSValue::encode(jsSingleCharacterString(exec, exec->argument(0).toUInt32(exec))); + return JSValue::encode(stringFromCharCodeSlowCase(exec)); +} + +ASSERT_CLASS_FITS_IN_CELL(StringConstructor); + +StringConstructor::StringConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, StringPrototype* stringPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, stringPrototype->classInfo()->className)) +{ + // ECMA 15.5.3.1 String.prototype + putDirectWithoutTransition(exec->propertyNames().prototype, stringPrototype, ReadOnly | DontEnum | DontDelete); + + // ECMA 15.5.3.2 fromCharCode() +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().fromCharCode, exec->globalData().getHostFunction(stringFromCharCode, fromCharCodeThunkGenerator)), DontEnum); +#else + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().fromCharCode, stringFromCharCode), DontEnum); +#endif + // no. of arguments for constructor + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +// ECMA 15.5.2 +static EncodedJSValue JSC_HOST_CALL constructWithStringConstructor(ExecState* exec) +{ + if (!exec->argumentCount()) + return JSValue::encode(new (exec) StringObject(exec, exec->lexicalGlobalObject()->stringObjectStructure())); + return JSValue::encode(new (exec) StringObject(exec, exec->lexicalGlobalObject()->stringObjectStructure(), exec->argument(0).toString(exec))); +} + +ConstructType StringConstructor::getConstructData(ConstructData& constructData) +{ + constructData.native.function = constructWithStringConstructor; + return ConstructTypeHost; +} + +// ECMA 15.5.1 +static EncodedJSValue JSC_HOST_CALL callStringConstructor(ExecState* exec) +{ + if (!exec->argumentCount()) + return JSValue::encode(jsEmptyString(exec)); + return JSValue::encode(jsString(exec, exec->argument(0).toString(exec))); +} + +CallType StringConstructor::getCallData(CallData& callData) +{ + callData.native.function = callStringConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringConstructor.h b/Source/JavaScriptCore/runtime/StringConstructor.h new file mode 100644 index 0000000..20f3a52 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringConstructor.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringConstructor_h +#define StringConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + + class StringPrototype; + + class StringConstructor : public InternalFunction { + public: + StringConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, StringPrototype*); + + virtual ConstructType getConstructData(ConstructData&); + virtual CallType getCallData(CallData&); + }; + +} // namespace JSC + +#endif // StringConstructor_h diff --git a/Source/JavaScriptCore/runtime/StringObject.cpp b/Source/JavaScriptCore/runtime/StringObject.cpp new file mode 100644 index 0000000..dc27618 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringObject.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringObject.h" + +#include "PropertyNameArray.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(StringObject); + +const ClassInfo StringObject::info = { "String", 0, 0, 0 }; + +StringObject::StringObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) + : JSWrapperObject(structure) +{ + setInternalValue(jsEmptyString(exec)); +} + +StringObject::StringObject(NonNullPassRefPtr<Structure> structure, JSString* string) + : JSWrapperObject(structure) +{ + setInternalValue(string); +} + +StringObject::StringObject(ExecState* exec, NonNullPassRefPtr<Structure> structure, const UString& string) + : JSWrapperObject(structure) +{ + setInternalValue(jsString(exec, string)); +} + +bool StringObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (internalValue()->getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool StringObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + if (internalValue()->getStringPropertySlot(exec, propertyName, slot)) + return true; + 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) + return; + JSObject::put(exec, propertyName, value, slot); +} + +bool StringObject::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().length) + return false; + bool isStrictUInt32; + unsigned i = propertyName.toUInt32(isStrictUInt32); + if (isStrictUInt32 && internalValue()->canGetIndex(i)) + return false; + return JSObject::deleteProperty(exec, propertyName); +} + +void StringObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + int size = internalValue()->length(); + for (int i = 0; i < size; ++i) + propertyNames.add(Identifier(exec, UString::number(i))); + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().length); + return JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringObject.h b/Source/JavaScriptCore/runtime/StringObject.h new file mode 100644 index 0000000..e3add77 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringObject.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringObject_h +#define StringObject_h + +#include "JSWrapperObject.h" +#include "JSString.h" + +namespace JSC { + + class StringObject : public JSWrapperObject { + public: + 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 getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + virtual const ClassInfo* classInfo() const { return &info; } + static const JS_EXPORTDATA ClassInfo info; + + JSString* internalValue() const { return asString(JSWrapperObject::internalValue());} + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSWrapperObject::StructureFlags; + StringObject(NonNullPassRefPtr<Structure>, JSString*); + }; + + StringObject* asStringObject(JSValue); + + inline StringObject* asStringObject(JSValue value) + { + ASSERT(asObject(value)->inherits(&StringObject::info)); + return static_cast<StringObject*>(asObject(value)); + } + +} // namespace JSC + +#endif // StringObject_h diff --git a/Source/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h b/Source/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h new file mode 100644 index 0000000..43c3e38 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringObjectThatMasqueradesAsUndefined_h +#define StringObjectThatMasqueradesAsUndefined_h + +#include "JSGlobalObject.h" +#include "StringObject.h" +#include "UString.h" + +namespace JSC { + + // WebCore uses this to make style.filter undetectable + class StringObjectThatMasqueradesAsUndefined : public StringObject { + public: + static StringObjectThatMasqueradesAsUndefined* create(ExecState* exec, const UString& string) + { + return new (exec) StringObjectThatMasqueradesAsUndefined(exec, + createStructure(exec->lexicalGlobalObject()->stringPrototype()), string); + } + + private: + 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, StructureFlags), AnonymousSlotCount); + } + + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | MasqueradesAsUndefined | OverridesGetPropertyNames | StringObject::StructureFlags; + + virtual bool toBoolean(ExecState*) const { return false; } + }; + +} // namespace JSC + +#endif // StringObjectThatMasqueradesAsUndefined_h diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp new file mode 100644 index 0000000..8b3d056 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -0,0 +1,1155 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringPrototype.h" + +#include "CachedCall.h" +#include "Error.h" +#include "Executable.h" +#include "JSGlobalObjectFunctions.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "JSStringBuilder.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "Operations.h" +#include "PropertyNameArray.h" +#include "RegExpCache.h" +#include "RegExpConstructor.h" +#include "RegExpObject.h" +#include <wtf/ASCIICType.h> +#include <wtf/MathExtras.h> +#include <wtf/unicode/Collator.h> + +using namespace WTF; + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(StringPrototype); + +static EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*); + +} + +#include "StringPrototype.lut.h" + +namespace JSC { + +const ClassInfo StringPrototype::info = { "String", &StringObject::info, 0, ExecState::stringTable }; + +/* Source for StringPrototype.lut.h +@begin stringTable 26 + toString stringProtoFuncToString DontEnum|Function 0 + valueOf stringProtoFuncToString DontEnum|Function 0 + charAt stringProtoFuncCharAt DontEnum|Function 1 + charCodeAt stringProtoFuncCharCodeAt DontEnum|Function 1 + concat stringProtoFuncConcat DontEnum|Function 1 + indexOf stringProtoFuncIndexOf DontEnum|Function 1 + lastIndexOf stringProtoFuncLastIndexOf DontEnum|Function 1 + match stringProtoFuncMatch DontEnum|Function 1 + replace stringProtoFuncReplace DontEnum|Function 2 + search stringProtoFuncSearch DontEnum|Function 1 + slice stringProtoFuncSlice DontEnum|Function 2 + split stringProtoFuncSplit DontEnum|Function 2 + substr stringProtoFuncSubstr DontEnum|Function 2 + substring stringProtoFuncSubstring DontEnum|Function 2 + toLowerCase stringProtoFuncToLowerCase DontEnum|Function 0 + toUpperCase stringProtoFuncToUpperCase DontEnum|Function 0 + localeCompare stringProtoFuncLocaleCompare DontEnum|Function 1 + + # toLocaleLowerCase and toLocaleUpperCase are currently identical to toLowerCase and toUpperCase + toLocaleLowerCase stringProtoFuncToLowerCase DontEnum|Function 0 + toLocaleUpperCase stringProtoFuncToUpperCase DontEnum|Function 0 + + big stringProtoFuncBig DontEnum|Function 0 + small stringProtoFuncSmall DontEnum|Function 0 + blink stringProtoFuncBlink DontEnum|Function 0 + bold stringProtoFuncBold DontEnum|Function 0 + fixed stringProtoFuncFixed DontEnum|Function 0 + italics stringProtoFuncItalics DontEnum|Function 0 + strike stringProtoFuncStrike DontEnum|Function 0 + sub stringProtoFuncSub DontEnum|Function 0 + sup stringProtoFuncSup DontEnum|Function 0 + fontcolor stringProtoFuncFontcolor DontEnum|Function 1 + fontsize stringProtoFuncFontsize DontEnum|Function 1 + anchor stringProtoFuncAnchor DontEnum|Function 1 + link stringProtoFuncLink DontEnum|Function 1 + trim stringProtoFuncTrim DontEnum|Function 0 + trimLeft stringProtoFuncTrimLeft DontEnum|Function 0 + trimRight stringProtoFuncTrimRight DontEnum|Function 0 +@end +*/ + +// ECMA 15.5.4 +StringPrototype::StringPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : StringObject(exec, structure) +{ + putAnonymousValue(0, globalObject); + // The constructor will be added later, after StringConstructor has been built + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +bool StringPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + 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 NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, size_t i) +{ + Vector<UChar> substitutedReplacement; + int offset = 0; + do { + if (i + 1 == replacement.length()) + break; + + UChar ref = replacement[i + 1]; + if (ref == '$') { + // "$$" -> "$" + ++i; + substitutedReplacement.append(replacement.characters() + offset, i - offset); + offset = i + 1; + continue; + } + + int backrefStart; + int backrefLength; + int advance = 0; + if (ref == '&') { + backrefStart = ovector[0]; + backrefLength = ovector[1] - backrefStart; + } else if (ref == '`') { + backrefStart = 0; + backrefLength = ovector[0]; + } else if (ref == '\'') { + backrefStart = ovector[1]; + backrefLength = source.length() - backrefStart; + } else if (reg && ref >= '0' && ref <= '9') { + // 1- and 2-digit back references are allowed + unsigned backrefIndex = ref - '0'; + if (backrefIndex > reg->numSubpatterns()) + continue; + if (replacement.length() > i + 2) { + ref = replacement[i + 2]; + if (ref >= '0' && ref <= '9') { + backrefIndex = 10 * backrefIndex + ref - '0'; + if (backrefIndex > reg->numSubpatterns()) + backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference + else + advance = 1; + } + } + if (!backrefIndex) + continue; + backrefStart = ovector[2 * backrefIndex]; + backrefLength = ovector[2 * backrefIndex + 1] - backrefStart; + } else + continue; + + if (i - offset) + substitutedReplacement.append(replacement.characters() + offset, i - offset); + i += 1 + advance; + offset = i + 1; + if (backrefStart >= 0) + substitutedReplacement.append(source.characters() + backrefStart, backrefLength); + } while ((i = replacement.find('$', i + 1)) != notFound); + + if (replacement.length() - offset) + substitutedReplacement.append(replacement.characters() + offset, replacement.length() - offset); + + substitutedReplacement.shrinkToFit(); + return UString::adopt(substitutedReplacement); +} + +static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg) +{ + size_t i = replacement.find('$', 0); + if (UNLIKELY(i != notFound)) + return substituteBackreferencesSlow(replacement, source, ovector, reg, i); + return replacement; +} + +static inline int localeCompare(const UString& a, const UString& b) +{ + return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.characters()), a.length(), reinterpret_cast<const ::UChar*>(b.characters()), b.length()); +} + +struct StringRange { +public: + StringRange(int pos, int len) + : position(pos) + , length(len) + { + } + + StringRange() + { + } + + int position; + int length; +}; + +static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount) +{ + if (rangeCount == 1 && separatorCount == 0) { + int sourceSize = source.length(); + int position = substringRanges[0].position; + int length = substringRanges[0].length; + if (position <= 0 && length >= sourceSize) + return sourceVal; + // We could call UString::substr, but this would result in redundant checks + return jsString(exec, StringImpl::create(source.impl(), max(0, position), min(sourceSize, length))); + } + + int totalLength = 0; + for (int i = 0; i < rangeCount; i++) + totalLength += substringRanges[i].length; + for (int i = 0; i < separatorCount; i++) + totalLength += separators[i].length(); + + if (totalLength == 0) + return jsString(exec, ""); + + UChar* buffer; + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int maxCount = max(rangeCount, separatorCount); + int bufferPos = 0; + for (int i = 0; i < maxCount; i++) { + if (i < rangeCount) { + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, source.characters() + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + if (i < separatorCount) { + if (int sepLen = separators[i].length()) { + StringImpl::copyChars(buffer + bufferPos, separators[i].characters(), sepLen); + bufferPos += sepLen; + } + } + } + + return jsString(exec, impl); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSString* sourceVal = thisValue.toThisJSString(exec); + JSValue pattern = exec->argument(0); + JSValue replacement = exec->argument(1); + + UString replacementString; + CallData callData; + CallType callType = getCallData(replacement, callData); + if (callType == CallTypeNone) + replacementString = replacement.toString(exec); + + if (pattern.inherits(&RegExpObject::info)) { + const UString& source = sourceVal->value(exec); + unsigned sourceLen = source.length(); + if (exec->hadException()) + return JSValue::encode(JSValue()); + RegExp* reg = asRegExpObject(pattern)->regExp(); + bool global = reg->global(); + + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + + int lastIndex = 0; + unsigned startPosition = 0; + + Vector<StringRange, 16> sourceRanges; + Vector<UString, 16> replacements; + + // This is either a loop (if global is set) or a one-way (if not). + if (global && callType == CallTypeJS) { + // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue + int argCount = reg->numSubpatterns() + 1 + 2; + JSFunction* func = asFunction(replacement); + CachedCall cachedCall(exec, func, argCount); + if (exec->hadException()) + return JSValue::encode(jsNull()); + while (true) { + int matchIndex; + int matchLen = 0; + int* ovector; + regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + int completeMatchStart = ovector[0]; + unsigned i = 0; + for (; i < reg->numSubpatterns() + 1; ++i) { + int matchStart = ovector[i * 2]; + int matchLen = ovector[i * 2 + 1] - matchStart; + + if (matchStart < 0) + cachedCall.setArgument(i, jsUndefined()); + else + cachedCall.setArgument(i, jsSubstring(exec, source, matchStart, matchLen)); + } + + cachedCall.setArgument(i++, jsNumber(completeMatchStart)); + cachedCall.setArgument(i++, sourceVal); + + cachedCall.setThis(exec->globalThisValue()); + JSValue result = cachedCall.call(); + if (LIKELY(result.isString())) + replacements.append(asString(result)->value(exec)); + else + replacements.append(result.toString(cachedCall.newCallFrame(exec))); + if (exec->hadException()) + break; + + lastIndex = matchIndex + matchLen; + startPosition = lastIndex; + + // special case of empty match + if (matchLen == 0) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + } else { + do { + int matchIndex; + int matchLen = 0; + int* ovector; + regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + if (callType != CallTypeNone) { + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + int completeMatchStart = ovector[0]; + MarkedArgumentBuffer args; + + for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) { + int matchStart = ovector[i * 2]; + int matchLen = ovector[i * 2 + 1] - matchStart; + + if (matchStart < 0) + args.append(jsUndefined()); + else + args.append(jsSubstring(exec, source, matchStart, matchLen)); + } + + args.append(jsNumber(completeMatchStart)); + args.append(sourceVal); + + replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec)); + if (exec->hadException()) + break; + } else { + int replLen = replacementString.length(); + if (lastIndex < matchIndex || replLen) { + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + if (replLen) + replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); + else + replacements.append(UString()); + } + } + + lastIndex = matchIndex + matchLen; + startPosition = lastIndex; + + // special case of empty match + if (matchLen == 0) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } while (global); + } + + if (!lastIndex && replacements.isEmpty()) + return JSValue::encode(sourceVal); + + if (static_cast<unsigned>(lastIndex) < sourceLen) + sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); + + return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); + } + + // Not a regular expression, so treat the pattern as a string. + + UString patternString = pattern.toString(exec); + // Special case for single character patterns without back reference replacement + if (patternString.length() == 1 && callType == CallTypeNone && replacementString.find('$', 0) == notFound) + return JSValue::encode(sourceVal->replaceCharacter(exec, patternString[0], replacementString)); + + const UString& source = sourceVal->value(exec); + size_t matchPos = source.find(patternString); + + if (matchPos == notFound) + return JSValue::encode(sourceVal); + + int matchLen = patternString.length(); + if (callType != CallTypeNone) { + MarkedArgumentBuffer args; + args.append(jsSubstring(exec, source, matchPos, matchLen)); + args.append(jsNumber(matchPos)); + args.append(sourceVal); + + replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec); + } + + size_t matchEnd = matchPos + matchLen; + int ovector[2] = { matchPos, matchEnd }; + return JSValue::encode(jsString(exec, source.substringSharingImpl(0, matchPos), substituteBackreferences(replacementString, source, ovector, 0), source.substringSharingImpl(matchEnd))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + // Also used for valueOf. + + if (thisValue.isString()) + return JSValue::encode(thisValue); + + if (thisValue.inherits(&StringObject::info)) + return JSValue::encode(asStringObject(thisValue)->internalValue()); + + return throwVMTypeError(exec); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + unsigned len = s.length(); + JSValue a0 = exec->argument(0); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); + if (i < len) + return JSValue::encode(jsSingleCharacterSubstring(exec, s, i)); + return JSValue::encode(jsEmptyString(exec)); + } + double dpos = a0.toInteger(exec); + if (dpos >= 0 && dpos < len) + return JSValue::encode(jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos))); + return JSValue::encode(jsEmptyString(exec)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + unsigned len = s.length(); + JSValue a0 = exec->argument(0); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); + if (i < len) + return JSValue::encode(jsNumber(s.characters()[i])); + return JSValue::encode(jsNaN()); + } + double dpos = a0.toInteger(exec); + if (dpos >= 0 && dpos < len) + return JSValue::encode(jsNumber(s[static_cast<int>(dpos)])); + return JSValue::encode(jsNaN()); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isString() && (exec->argumentCount() == 1)) { + JSValue v = exec->argument(0); + return JSValue::encode(v.isString() + ? jsString(exec, asString(thisValue), asString(v)) + : jsString(exec, asString(thisValue), v.toString(exec))); + } + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + return JSValue::encode(jsString(exec, thisValue)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + int len = s.length(); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + UString u2 = a0.toString(exec); + int pos; + if (a1.isUndefined()) + pos = 0; + else if (a1.isUInt32()) + pos = min<uint32_t>(a1.asUInt32(), len); + else { + double dpos = a1.toInteger(exec); + if (dpos < 0) + dpos = 0; + else if (dpos > len) + dpos = len; + pos = static_cast<int>(dpos); + } + + size_t result = s.find(u2, pos); + if (result == notFound) + return JSValue::encode(jsNumber(-1)); + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + int len = s.length(); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + UString u2 = a0.toString(exec); + double dpos = a1.toIntegerPreserveNaN(exec); + if (dpos < 0) + dpos = 0; + else if (!(dpos <= len)) // true for NaN + dpos = len; +#if OS(SYMBIAN) + // Work around for broken NaN compare operator + else if (isnan(dpos)) + dpos = len; +#endif + + size_t result = s.reverseFind(u2, static_cast<unsigned>(dpos)); + if (result == notFound) + return JSValue::encode(jsNumber(-1)); + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + + JSValue a0 = exec->argument(0); + + UString u = s; + RefPtr<RegExp> reg; + RegExpObject* imp = 0; + if (a0.inherits(&RegExpObject::info)) + reg = asRegExpObject(a0)->regExp(); + else { + /* + * ECMA 15.5.4.12 String.prototype.search (regexp) + * If regexp is not an object whose [[Class]] property is "RegExp", it is + * replaced with the result of the expression new RegExp(regexp). + */ + reg = exec->globalData().regExpCache()->lookupOrCreate(a0.toString(exec), UString()); + } + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + int pos; + int matchLength = 0; + regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength); + if (!(reg->global())) { + // case without 'g' flag is handled like RegExp.prototype.exec + if (pos < 0) + return JSValue::encode(jsNull()); + return JSValue::encode(regExpConstructor->arrayOfMatches(exec)); + } + + // return array of matches + MarkedArgumentBuffer list; + int lastIndex = 0; + while (pos >= 0) { + list.append(jsSubstring(exec, u, pos, matchLength)); + lastIndex = pos; + pos += matchLength == 0 ? 1 : matchLength; + regExpConstructor->performMatch(reg.get(), u, pos, pos, matchLength); + } + if (imp) + imp->setLastIndex(lastIndex); + if (list.isEmpty()) { + // if there are no matches at all, it's important to return + // Null instead of an empty array, because this matches + // other browsers and because Null is a false value. + return JSValue::encode(jsNull()); + } + + return JSValue::encode(constructArray(exec, list)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + + JSValue a0 = exec->argument(0); + + UString u = s; + RefPtr<RegExp> reg; + if (a0.inherits(&RegExpObject::info)) + reg = asRegExpObject(a0)->regExp(); + else { + /* + * ECMA 15.5.4.12 String.prototype.search (regexp) + * If regexp is not an object whose [[Class]] property is "RegExp", it is + * replaced with the result of the expression new RegExp(regexp). + */ + reg = exec->globalData().regExpCache()->lookupOrCreate(a0.toString(exec), UString()); + } + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + int pos; + int matchLength = 0; + regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength); + return JSValue::encode(jsNumber(pos)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + int len = s.length(); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + // The arg processing is very much like ArrayProtoFunc::Slice + double start = a0.toInteger(exec); + double end = a1.isUndefined() ? len : a1.toInteger(exec); + double from = start < 0 ? len + start : start; + double to = end < 0 ? len + end : end; + if (to > from && to > 0 && from < len) { + if (from < 0) + from = 0; + if (to > len) + to = len; + return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from))); + } + + return JSValue::encode(jsEmptyString(exec)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toThisString(exec); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + JSArray* result = constructEmptyArray(exec); + unsigned i = 0; + unsigned p0 = 0; + unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec); + 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 + return JSValue::encode(result); + } + unsigned pos = 0; + while (i != limit && pos < s.length()) { + Vector<int, 32> ovector; + int mpos = reg->match(s, pos, &ovector); + if (mpos < 0) + break; + int mlen = ovector[1] - ovector[0]; + pos = mpos + (mlen == 0 ? 1 : mlen); + if (static_cast<unsigned>(mpos) != p0 || mlen) { + result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0)); + p0 = mpos + mlen; + } + for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) { + int spos = ovector[si * 2]; + if (spos < 0) + result->put(exec, i++, jsUndefined()); + else + result->put(exec, i++, jsSubstring(exec, s, spos, ovector[si * 2 + 1] - spos)); + } + } + } else { + UString u2 = a0.toString(exec); + if (u2.isEmpty()) { + if (s.isEmpty()) { + // empty separator matches empty string -> empty array + return JSValue::encode(result); + } + while (i != limit && p0 < s.length() - 1) + result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++)); + } else { + size_t pos; + while (i != limit && (pos = s.find(u2, p0)) != notFound) { + result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0)); + p0 = pos + u2.length(); + } + } + } + + // add remaining string + if (i != limit) + result->put(exec, i++, jsSubstring(exec, s, p0, s.length() - p0)); + + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + unsigned len; + JSString* jsString = 0; + UString uString; + if (thisValue.isString()) { + jsString = static_cast<JSString*>(thisValue.asCell()); + len = jsString->length(); + } else { + uString = thisValue.toThisObject(exec)->toString(exec); + len = uString.length(); + } + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + double start = a0.toInteger(exec); + double length = a1.isUndefined() ? len : a1.toInteger(exec); + if (start >= len || length <= 0) + return JSValue::encode(jsEmptyString(exec)); + if (start < 0) { + start += len; + if (start < 0) + start = 0; + } + if (start + length > len) + length = len - start; + unsigned substringStart = static_cast<unsigned>(start); + unsigned substringLength = static_cast<unsigned>(length); + if (jsString) + return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength)); + return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + int len; + JSString* jsString = 0; + UString uString; + if (thisValue.isString()) { + jsString = static_cast<JSString*>(thisValue.asCell()); + len = jsString->length(); + } else { + uString = thisValue.toThisObject(exec)->toString(exec); + len = uString.length(); + } + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + double start = a0.toNumber(exec); + double end; + if (!(start >= 0)) // check for negative values or NaN + start = 0; + else if (start > len) + start = len; + if (a1.isUndefined()) + end = len; + else { + end = a1.toNumber(exec); + if (!(end >= 0)) // check for negative values or NaN + end = 0; + else if (end > len) + end = len; + } + if (start > end) { + double temp = end; + end = start; + start = temp; + } + unsigned substringStart = static_cast<unsigned>(start); + unsigned substringLength = static_cast<unsigned>(end) - substringStart; + if (jsString) + return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength)); + return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + JSString* sVal = thisValue.toThisJSString(exec); + const UString& s = sVal->value(exec); + + int sSize = s.length(); + if (!sSize) + return JSValue::encode(sVal); + + const UChar* sData = s.characters(); + Vector<UChar> buffer(sSize); + + UChar ored = 0; + for (int i = 0; i < sSize; i++) { + UChar c = sData[i]; + ored |= c; + buffer[i] = toASCIILower(c); + } + if (!(ored & ~0x7f)) + return JSValue::encode(jsString(exec, UString::adopt(buffer))); + + bool error; + int length = Unicode::toLower(buffer.data(), sSize, sData, sSize, &error); + if (error) { + buffer.resize(length); + length = Unicode::toLower(buffer.data(), length, sData, sSize, &error); + if (error) + return JSValue::encode(sVal); + } + if (length == sSize) { + if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0) + return JSValue::encode(sVal); + } else + buffer.resize(length); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + JSString* sVal = thisValue.toThisJSString(exec); + const UString& s = sVal->value(exec); + + int sSize = s.length(); + if (!sSize) + return JSValue::encode(sVal); + + const UChar* sData = s.characters(); + Vector<UChar> buffer(sSize); + + UChar ored = 0; + for (int i = 0; i < sSize; i++) { + UChar c = sData[i]; + ored |= c; + buffer[i] = toASCIIUpper(c); + } + if (!(ored & ~0x7f)) + return JSValue::encode(jsString(exec, UString::adopt(buffer))); + + bool error; + int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error); + if (error) { + buffer.resize(length); + length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error); + if (error) + return JSValue::encode(sVal); + } + if (length == sSize) { + if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0) + return JSValue::encode(sVal); + } else + buffer.resize(length); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec) +{ + if (exec->argumentCount() < 1) + return JSValue::encode(jsNumber(0)); + + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + + UString s = thisValue.toThisString(exec); + JSValue a0 = exec->argument(0); + return JSValue::encode(jsNumber(localeCompare(s, a0.toString(exec)))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + JSValue a0 = exec->argument(0); + return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + JSValue a0 = exec->argument(0); + + uint32_t smallInteger; + if (a0.getUInt32(smallInteger) && smallInteger <= 9) { + unsigned stringSize = s.length(); + unsigned bufferSize = 22 + stringSize; + UChar* buffer; + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer); + if (!impl) + return JSValue::encode(jsUndefined()); + buffer[0] = '<'; + buffer[1] = 'f'; + buffer[2] = 'o'; + buffer[3] = 'n'; + buffer[4] = 't'; + buffer[5] = ' '; + buffer[6] = 's'; + buffer[7] = 'i'; + buffer[8] = 'z'; + buffer[9] = 'e'; + buffer[10] = '='; + buffer[11] = '"'; + buffer[12] = '0' + smallInteger; + buffer[13] = '"'; + buffer[14] = '>'; + memcpy(&buffer[15], s.characters(), stringSize * sizeof(UChar)); + buffer[15 + stringSize] = '<'; + buffer[16 + stringSize] = '/'; + buffer[17 + stringSize] = 'f'; + buffer[18 + stringSize] = 'o'; + buffer[19 + stringSize] = 'n'; + buffer[20 + stringSize] = 't'; + buffer[21 + stringSize] = '>'; + return JSValue::encode(jsNontrivialString(exec, impl)); + } + + return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + JSValue a0 = exec->argument(0); + return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + UString s = thisValue.toThisString(exec); + JSValue a0 = exec->argument(0); + UString linkText = a0.toString(exec); + + unsigned linkTextSize = linkText.length(); + unsigned stringSize = s.length(); + unsigned bufferSize = 15 + linkTextSize + stringSize; + UChar* buffer; + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer); + if (!impl) + return JSValue::encode(jsUndefined()); + buffer[0] = '<'; + buffer[1] = 'a'; + buffer[2] = ' '; + buffer[3] = 'h'; + buffer[4] = 'r'; + buffer[5] = 'e'; + buffer[6] = 'f'; + buffer[7] = '='; + buffer[8] = '"'; + memcpy(&buffer[9], linkText.characters(), linkTextSize * sizeof(UChar)); + buffer[9 + linkTextSize] = '"'; + buffer[10 + linkTextSize] = '>'; + memcpy(&buffer[11 + linkTextSize], s.characters(), stringSize * sizeof(UChar)); + buffer[11 + linkTextSize + stringSize] = '<'; + buffer[12 + linkTextSize + stringSize] = '/'; + buffer[13 + linkTextSize + stringSize] = 'a'; + buffer[14 + linkTextSize + stringSize] = '>'; + return JSValue::encode(jsNontrivialString(exec, impl)); +} + +enum { + TrimLeft = 1, + TrimRight = 2 +}; + +static inline bool isTrimWhitespace(UChar c) +{ + return isStrWhiteSpace(c) || c == 0x200b; +} + +static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind) +{ + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwTypeError(exec); + UString str = thisValue.toThisString(exec); + unsigned left = 0; + if (trimKind & TrimLeft) { + while (left < str.length() && isTrimWhitespace(str[left])) + left++; + } + unsigned right = str.length(); + if (trimKind & TrimRight) { + while (right > left && isTrimWhitespace(str[right - 1])) + right--; + } + + // Don't gc allocate a new string if we don't have to. + if (left == 0 && right == str.length() && thisValue.isString()) + return thisValue; + + return jsString(exec, str.substringSharingImpl(left, right - left)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimLeft)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimRight)); +} + + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringPrototype.h b/Source/JavaScriptCore/runtime/StringPrototype.h new file mode 100644 index 0000000..4b0f88f --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringPrototype.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 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 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringPrototype_h +#define StringPrototype_h + +#include "StringObject.h" + +namespace JSC { + + class ObjectPrototype; + + class StringPrototype : public StringObject { + public: + StringPrototype(ExecState*, JSGlobalObject*, 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; + }; + +} // namespace JSC + +#endif // StringPrototype_h diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp new file mode 100644 index 0000000..0179eed --- /dev/null +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -0,0 +1,1270 @@ +/* + * 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 + * 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 COMPUTER, 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 COMPUTER, 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 "Structure.h" + +#include "Identifier.h" +#include "JSObject.h" +#include "JSPropertyNameIterator.h" +#include "Lookup.h" +#include "PropertyNameArray.h" +#include "StructureChain.h" +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/RefPtr.h> + +#if ENABLE(JSC_MULTIPLE_THREADS) +#include <wtf/Threading.h> +#endif + +#define DUMP_STRUCTURE_ID_STATISTICS 0 + +#ifndef NDEBUG +#define DO_PROPERTYMAP_CONSTENCY_CHECK 0 +#else +#define DO_PROPERTYMAP_CONSTENCY_CHECK 0 +#endif + +using namespace std; +using namespace WTF; + +namespace JSC { + +// Choose a number for the following so that most property maps are smaller, +// but it's not going to blow out the stack to allocate this number of pointers. +static const int smallMapThreshold = 1024; + +// The point at which the function call overhead of the qsort implementation +// becomes small compared to the inefficiency of insertion sort. +static const unsigned tinyMapThreshold = 20; + +static const unsigned newTableSize = 16; + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter structureCounter("Structure"); + +#if ENABLE(JSC_MULTIPLE_THREADS) +static Mutex& ignoreSetMutex = *(new Mutex); +#endif + +static bool shouldIgnoreLeaks; +static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>); +#endif + +#if DUMP_STRUCTURE_ID_STATISTICS +static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); +#endif + +static int comparePropertyMapEntryIndices(const void* a, const void* b); + +inline void Structure::setTransitionTable(TransitionTable* table) +{ + ASSERT(m_isUsingSingleSlot); +#ifndef NDEBUG + setSingleTransition(0); +#endif + m_isUsingSingleSlot = false; + m_transitions.m_table = table; + // This implicitly clears the flag that indicates we're using a single transition + ASSERT(!m_isUsingSingleSlot); +} + +// 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 Structure::transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + 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 = transitionTable()->find(key); + if (find == transitionTable()->end()) + return false; + + return find->second.first || find->second.second->transitionedFor(specificValue); +} + +inline Structure* Structure::transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const +{ + if (m_isUsingSingleSlot) { + 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 = transitionTable()->get(key); + if (transition.second && transition.second->transitionedFor(specificValue)) + return transition.second; + return transition.first; +} + +inline bool Structure::transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const +{ + if (m_isUsingSingleSlot) { + Structure* transition = singleTransition(); + return transition && transition->m_nameInPrevious == key.first + && transition->m_attributesInPrevious == key.second; + } + return transitionTable()->contains(key); +} + +inline void Structure::transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + ASSERT(transitionTableContains(key, specificValue)); + setSingleTransition(0); + return; + } + TransitionTable::iterator find = transitionTable()->find(key); + if (!specificValue) + find->second.first = 0; + else + find->second.second = 0; + if (!find->second.first && !find->second.second) + transitionTable()->remove(find); +} + +inline void Structure::transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + if (!singleTransition()) { + setSingleTransition(structure); + return; + } + Structure* existingTransition = singleTransition(); + TransitionTable* transitionTable = new TransitionTable; + setTransitionTable(transitionTable); + if (existingTransition) + transitionTableAdd(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); + } + if (!specificValue) { + TransitionTable::iterator find = transitionTable()->find(key); + if (find == transitionTable()->end()) + transitionTable()->add(key, Transition(structure, static_cast<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(!transitionTable()->contains(key)); + transitionTable()->add(key, Transition(static_cast<Structure*>(0), structure)); + } +} + +void Structure::dumpStatistics() +{ +#if DUMP_STRUCTURE_ID_STATISTICS + unsigned numberLeaf = 0; + unsigned numberUsingSingleSlot = 0; + unsigned numberSingletons = 0; + unsigned numberWithPropertyMaps = 0; + unsigned totalPropertyMapsSize = 0; + + HashSet<Structure*>::const_iterator end = liveStructureSet.end(); + for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { + Structure* structure = *it; + if (structure->m_usingSingleTransitionSlot) { + if (!structure->m_transitions.singleTransition) + ++numberLeaf; + else + ++numberUsingSingleSlot; + + if (!structure->m_previous && !structure->m_transitions.singleTransition) + ++numberSingletons; + } + + if (structure->m_propertyTable) { + ++numberWithPropertyMaps; + totalPropertyMapsSize += PropertyMapHashTable::allocationSize(structure->m_propertyTable->size); + if (structure->m_propertyTable->deletedOffsets) + totalPropertyMapsSize += (structure->m_propertyTable->deletedOffsets->capacity() * sizeof(unsigned)); + } + } + + printf("Number of live Structures: %d\n", liveStructureSet.size()); + printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); + printf("Number of Structures that are leaf nodes: %d\n", numberLeaf); + printf("Number of Structures that singletons: %d\n", numberSingletons); + printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); + + printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); + printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize); + printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); +#else + printf("Dumping Structure statistics is not enabled.\n"); +#endif +} + +Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount) + : m_typeInfo(typeInfo) + , m_prototype(prototype) + , m_specificValueInPrevious(0) + , m_propertyTable(0) + , m_propertyStorageCapacity(JSObject::inlineStorageCapacity) + , m_offset(noOffset) + , m_dictionaryKind(NoneDictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(false) + , m_hasNonEnumerableProperties(false) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(0) + , m_anonymousSlotCount(anonymousSlotCount) + , m_isUsingSingleSlot(true) +{ + m_transitions.m_singleTransition = 0; + + ASSERT(m_prototype); + ASSERT(m_prototype.isObject() || m_prototype.isNull()); + +#ifndef NDEBUG +#if ENABLE(JSC_MULTIPLE_THREADS) + MutexLocker protect(ignoreSetMutex); +#endif + if (shouldIgnoreLeaks) + ignoreSet.add(this); + else + structureCounter.increment(); +#endif + +#if DUMP_STRUCTURE_ID_STATISTICS + liveStructureSet.add(this); +#endif +} + +Structure::~Structure() +{ + if (m_previous) { + ASSERT(m_nameInPrevious); + m_previous->transitionTableRemove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); + + } + ASSERT(!m_enumerationCache.hasDeadObject()); + + if (m_propertyTable) { + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (StringImpl* key = m_propertyTable->entries()[i].key) + key->deref(); + } + + delete m_propertyTable->deletedOffsets; + fastFree(m_propertyTable); + } + + if (!m_isUsingSingleSlot) + delete transitionTable(); + +#ifndef NDEBUG +#if ENABLE(JSC_MULTIPLE_THREADS) + MutexLocker protect(ignoreSetMutex); +#endif + HashSet<Structure*>::iterator it = ignoreSet.find(this); + if (it != ignoreSet.end()) + ignoreSet.remove(it); + else + structureCounter.decrement(); +#endif + +#if DUMP_STRUCTURE_ID_STATISTICS + liveStructureSet.remove(this); +#endif +} + +void Structure::startIgnoringLeaks() +{ +#ifndef NDEBUG + shouldIgnoreLeaks = true; +#endif +} + +void Structure::stopIgnoringLeaks() +{ +#ifndef NDEBUG + shouldIgnoreLeaks = false; +#endif +} + +static bool isPowerOf2(unsigned v) +{ + // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html + + return !(v & (v - 1)) && v; +} + +static unsigned nextPowerOf2(unsigned v) +{ + // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html + // Devised by Sean Anderson, Sepember 14, 2001 + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v; +} + +static unsigned sizeForKeyCount(size_t keyCount) +{ + if (keyCount == notFound) + return newTableSize; + + if (keyCount < 8) + return newTableSize; + + if (isPowerOf2(keyCount)) + return keyCount * 4; + + return nextPowerOf2(keyCount) * 2; +} + +void Structure::materializePropertyMap() +{ + ASSERT(!m_propertyTable); + + Vector<Structure*, 8> structures; + structures.append(this); + + Structure* structure = this; + + // Search for the last Structure with a property table. + while ((structure = structure->previousID())) { + if (structure->m_isPinnedPropertyTable) { + ASSERT(structure->m_propertyTable); + ASSERT(!structure->m_previous); + + m_propertyTable = structure->copyPropertyTable(); + break; + } + + structures.append(structure); + } + + if (!m_propertyTable) + createPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); + else { + if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size) + rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above. + } + + for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) { + structure = structures[i]; + structure->m_nameInPrevious->ref(); + PropertyMapEntry entry(structure->m_nameInPrevious.get(), m_anonymousSlotCount + structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed); + insertIntoPropertyMapHashTable(entry); + } +} + +void Structure::growPropertyStorageCapacity() +{ + if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity) + m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity; + else + m_propertyStorageCapacity *= 2; +} + +void Structure::despecifyDictionaryFunction(const Identifier& propertyName) +{ + const StringImpl* rep = propertyName.impl(); + + materializePropertyMapIfNecessary(); + + ASSERT(isDictionary()); + ASSERT(m_propertyTable); + + unsigned i = rep->existingHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + ASSERT(entryIndex != emptyEntryIndex); + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return; + } + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->existingHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + ASSERT(entryIndex != emptyEntryIndex); + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return; + } + } +} + +PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) +{ + ASSERT(!structure->isDictionary()); + ASSERT(structure->typeInfo().type() == ObjectType); + + if (Structure* existingTransition = structure->transitionTableGet(make_pair(propertyName.impl(), attributes), specificValue)) { + ASSERT(existingTransition->m_offset != noOffset); + offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount; + ASSERT(offset >= structure->m_anonymousSlotCount); + ASSERT(structure->m_anonymousSlotCount == existingTransition->m_anonymousSlotCount); + return existingTransition; + } + + return 0; +} + +PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) +{ + ASSERT(!structure->isDictionary()); + ASSERT(structure->typeInfo().type() == ObjectType); + ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); + + if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; + + if (structure->transitionCount() > s_maxTransitionLength) { + RefPtr<Structure> transition = toCacheableDictionaryTransition(structure); + ASSERT(structure != transition); + offset = transition->put(propertyName, attributes, specificValue); + ASSERT(offset >= structure->m_anonymousSlotCount); + ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount); + if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) + transition->growPropertyStorageCapacity(); + return transition.release(); + } + + RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount()); + + transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain; + transition->m_previous = structure; + transition->m_nameInPrevious = propertyName.impl(); + transition->m_attributesInPrevious = attributes; + transition->m_specificValueInPrevious = specificValue; + transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; + transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; + transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; + transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount; + + 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(); + } + + offset = transition->put(propertyName, attributes, specificValue); + ASSERT(offset >= structure->m_anonymousSlotCount); + ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount); + if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) + transition->growPropertyStorageCapacity(); + + transition->m_offset = offset - structure->m_anonymousSlotCount; + ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); + structure->transitionTableAdd(make_pair(propertyName.impl(), attributes), transition.get(), specificValue); + return transition.release(); +} + +PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset) +{ + ASSERT(!structure->isUncacheableDictionary()); + + RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure); + + offset = transition->remove(propertyName); + ASSERT(offset >= structure->m_anonymousSlotCount); + ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount); + + return transition.release(); +} + +PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype) +{ + RefPtr<Structure> transition = create(prototype, structure->typeInfo(), structure->anonymousSlotCount()); + + transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; + transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; + transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; + transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount; + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(); + transition->m_propertyTable = structure->copyPropertyTable(); + transition->m_isPinnedPropertyTable = true; + + ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); + return transition.release(); +} + +PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction) +{ + ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); + RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount()); + + transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; + transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; + transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; + transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount + 1; + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(); + transition->m_propertyTable = structure->copyPropertyTable(); + transition->m_isPinnedPropertyTable = true; + + if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + transition->despecifyAllFunctions(); + else { + bool removed = transition->despecifyFunction(replaceFunction); + ASSERT_UNUSED(removed, removed); + } + + ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); + return transition.release(); +} + +PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure) +{ + RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount()); + transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; + transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties; + transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; + transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount; + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(); + transition->m_propertyTable = structure->copyPropertyTable(); + transition->m_isPinnedPropertyTable = true; + + ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); + return transition.release(); +} + +PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind) +{ + ASSERT(!structure->isUncacheableDictionary()); + + RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount()); + transition->m_dictionaryKind = kind; + transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; + transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; + transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; + transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount; + + structure->materializePropertyMapIfNecessary(); + transition->m_propertyTable = structure->copyPropertyTable(); + transition->m_isPinnedPropertyTable = true; + + ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); + 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::flattenDictionaryStructure(JSObject* object) +{ + ASSERT(isDictionary()); + if (isUncacheableDictionary()) { + ASSERT(m_propertyTable); + Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount); + PropertyMapEntry** p = sortedPropertyEntries.data(); + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_propertyTable->entries()[i].key) + *p++ = &m_propertyTable->entries()[i]; + } + size_t propertyCount = p - sortedPropertyEntries.data(); + qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); + sortedPropertyEntries.resize(propertyCount); + + // We now have the properties currently defined on this object + // in the order that they are expected to be in, but we need to + // reorder the storage, so we have to copy the current values out + Vector<JSValue> values(propertyCount); + unsigned anonymousSlotCount = m_anonymousSlotCount; + for (unsigned i = 0; i < propertyCount; i++) { + PropertyMapEntry* entry = sortedPropertyEntries[i]; + values[i] = object->getDirectOffset(entry->offset); + // Update property table to have the new property offsets + entry->offset = anonymousSlotCount + i; + entry->index = i; + } + + // Copy the original property values into their final locations + for (unsigned i = 0; i < propertyCount; i++) + object->putDirectOffset(anonymousSlotCount + i, values[i]); + + if (m_propertyTable->deletedOffsets) { + delete m_propertyTable->deletedOffsets; + m_propertyTable->deletedOffsets = 0; + } + } + + m_dictionaryKind = NoneDictionaryKind; + return this; +} + +size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) +{ + ASSERT(!m_enumerationCache); + + if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; + + materializePropertyMapIfNecessary(); + + m_isPinnedPropertyTable = true; + + size_t offset = put(propertyName, attributes, specificValue); + ASSERT(offset >= m_anonymousSlotCount); + if (propertyStorageSize() > propertyStorageCapacity()) + growPropertyStorageCapacity(); + return offset; +} + +size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName) +{ + ASSERT(isUncacheableDictionary()); + ASSERT(!m_enumerationCache); + + materializePropertyMapIfNecessary(); + + m_isPinnedPropertyTable = true; + size_t offset = remove(propertyName); + ASSERT(offset >= m_anonymousSlotCount); + return offset; +} + +#if DUMP_PROPERTYMAP_STATS + +static int numProbes; +static int numCollisions; +static int numRehashes; +static int numRemoves; + +struct PropertyMapStatisticsExitLogger { + ~PropertyMapStatisticsExitLogger(); +}; + +static PropertyMapStatisticsExitLogger logger; + +PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() +{ + printf("\nJSC::PropertyMap statistics\n\n"); + printf("%d probes\n", numProbes); + printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); + printf("%d rehashes\n", numRehashes); + printf("%d removes\n", numRemoves); +} + +#endif + +static const unsigned deletedSentinelIndex = 1; + +#if !DO_PROPERTYMAP_CONSTENCY_CHECK + +inline void Structure::checkConsistency() +{ +} + +#endif + +PropertyMapHashTable* Structure::copyPropertyTable() +{ + if (!m_propertyTable) + return 0; + + size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size); + PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize)); + memcpy(newTable, m_propertyTable, tableSize); + + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; ++i) { + if (StringImpl* key = newTable->entries()[i].key) + key->ref(); + } + + // Copy the deletedOffsets vector. + if (m_propertyTable->deletedOffsets) + newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets); + + return newTable; +} + +size_t Structure::get(const StringImpl* rep, unsigned& attributes, JSCell*& specificValue) +{ + materializePropertyMapIfNecessary(); + if (!m_propertyTable) + return notFound; + + unsigned i = rep->existingHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return notFound; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + attributes = m_propertyTable->entries()[entryIndex - 1].attributes; + specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue; + ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount); + return m_propertyTable->entries()[entryIndex - 1].offset; + } + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->existingHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return notFound; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + attributes = m_propertyTable->entries()[entryIndex - 1].attributes; + specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue; + ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount); + return m_propertyTable->entries()[entryIndex - 1].offset; + } + } +} + +bool Structure::despecifyFunction(const Identifier& propertyName) +{ + ASSERT(!propertyName.isNull()); + + materializePropertyMapIfNecessary(); + if (!m_propertyTable) + return false; + + StringImpl* rep = propertyName.impl(); + + unsigned i = rep->existingHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return false; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return true; + } + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->existingHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return false; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return true; + } + } +} + +void Structure::despecifyAllFunctions() +{ + materializePropertyMapIfNecessary(); + if (!m_propertyTable) + return; + + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; ++i) + m_propertyTable->entries()[i].specificValue = 0; +} + +size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) +{ + ASSERT(!propertyName.isNull()); + ASSERT(get(propertyName) == notFound); + + checkConsistency(); + + if (attributes & DontEnum) + m_hasNonEnumerableProperties = true; + + StringImpl* rep = propertyName.impl(); + + if (!m_propertyTable) + createPropertyMapHashTable(); + + // FIXME: Consider a fast case for tables with no deleted sentinels. + + unsigned i = rep->existingHash(); + unsigned k = 0; + bool foundDeletedElement = false; + unsigned deletedElementIndex = 0; // initialize to make the compiler happy + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + while (1) { + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + break; + + if (entryIndex == deletedSentinelIndex) { + // If we find a deleted-element sentinel, remember it for use later. + if (!foundDeletedElement) { + foundDeletedElement = true; + deletedElementIndex = i; + } + } + + if (k == 0) { + k = 1 | doubleHash(rep->existingHash()); +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + } + + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } + + // Figure out which entry to use. + unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2; + if (foundDeletedElement) { + i = deletedElementIndex; + --m_propertyTable->deletedSentinelCount; + + // Since we're not making the table bigger, we can't use the entry one past + // the end that we were planning on using, so search backwards for the empty + // slot that we can use. We know it will be there because we did at least one + // deletion in the past that left an entry empty. + while (m_propertyTable->entries()[--entryIndex - 1].key) { } + } + + // Create a new hash table entry. + m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; + + // Create a new hash table entry. + rep->ref(); + m_propertyTable->entries()[entryIndex - 1].key = rep; + m_propertyTable->entries()[entryIndex - 1].attributes = attributes; + m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue; + m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed; + + unsigned newOffset; + if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) { + newOffset = m_propertyTable->deletedOffsets->last(); + m_propertyTable->deletedOffsets->removeLast(); + } else + newOffset = m_propertyTable->keyCount + m_anonymousSlotCount; + m_propertyTable->entries()[entryIndex - 1].offset = newOffset; + + ASSERT(newOffset >= m_anonymousSlotCount); + ++m_propertyTable->keyCount; + + if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size) + expandPropertyMapHashTable(); + + checkConsistency(); + return newOffset; +} + +bool Structure::hasTransition(StringImpl* rep, unsigned attributes) +{ + return transitionTableHasTransition(make_pair(rep, attributes)); +} + +size_t Structure::remove(const Identifier& propertyName) +{ + ASSERT(!propertyName.isNull()); + + checkConsistency(); + + StringImpl* rep = propertyName.impl(); + + if (!m_propertyTable) + return notFound; + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; + ++numRemoves; +#endif + + // Find the thing to remove. + unsigned i = rep->existingHash(); + unsigned k = 0; + unsigned entryIndex; + StringImpl* key = 0; + while (1) { + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return notFound; + + key = m_propertyTable->entries()[entryIndex - 1].key; + if (rep == key) + break; + + if (k == 0) { + k = 1 | doubleHash(rep->existingHash()); +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + } + + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } + + // Replace this one element with the deleted sentinel. Also clear out + // the entry so we can iterate all the entries as needed. + m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex; + + size_t offset = m_propertyTable->entries()[entryIndex - 1].offset; + ASSERT(offset >= m_anonymousSlotCount); + + key->deref(); + m_propertyTable->entries()[entryIndex - 1].key = 0; + m_propertyTable->entries()[entryIndex - 1].attributes = 0; + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + m_propertyTable->entries()[entryIndex - 1].offset = 0; + + if (!m_propertyTable->deletedOffsets) + m_propertyTable->deletedOffsets = new Vector<unsigned>; + m_propertyTable->deletedOffsets->append(offset); + + ASSERT(m_propertyTable->keyCount >= 1); + --m_propertyTable->keyCount; + ++m_propertyTable->deletedSentinelCount; + + if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size) + rehashPropertyMapHashTable(); + + checkConsistency(); + return offset; +} + +void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry) +{ + ASSERT(m_propertyTable); + ASSERT(entry.offset >= m_anonymousSlotCount); + unsigned i = entry.key->existingHash(); + unsigned k = 0; + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + while (1) { + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + break; + + if (k == 0) { + k = 1 | doubleHash(entry.key->existingHash()); +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + } + + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } + + unsigned entryIndex = m_propertyTable->keyCount + 2; + m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; + m_propertyTable->entries()[entryIndex - 1] = entry; + + ++m_propertyTable->keyCount; +} + +void Structure::createPropertyMapHashTable() +{ + ASSERT(sizeForKeyCount(7) == newTableSize); + createPropertyMapHashTable(newTableSize); +} + +void Structure::createPropertyMapHashTable(unsigned newTableSize) +{ + ASSERT(!m_propertyTable); + ASSERT(isPowerOf2(newTableSize)); + + checkConsistency(); + + m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); + m_propertyTable->size = newTableSize; + m_propertyTable->sizeMask = newTableSize - 1; + + checkConsistency(); +} + +void Structure::expandPropertyMapHashTable() +{ + ASSERT(m_propertyTable); + rehashPropertyMapHashTable(m_propertyTable->size * 2); +} + +void Structure::rehashPropertyMapHashTable() +{ + ASSERT(m_propertyTable); + ASSERT(m_propertyTable->size); + rehashPropertyMapHashTable(m_propertyTable->size); +} + +void Structure::rehashPropertyMapHashTable(unsigned newTableSize) +{ + ASSERT(m_propertyTable); + ASSERT(isPowerOf2(newTableSize)); + + checkConsistency(); + + PropertyMapHashTable* oldTable = m_propertyTable; + + m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); + m_propertyTable->size = newTableSize; + m_propertyTable->sizeMask = newTableSize - 1; + + unsigned lastIndexUsed = 0; + unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; ++i) { + if (oldTable->entries()[i].key) { + lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed); + insertIntoPropertyMapHashTable(oldTable->entries()[i]); + } + } + m_propertyTable->lastIndexUsed = lastIndexUsed; + m_propertyTable->deletedOffsets = oldTable->deletedOffsets; + + fastFree(oldTable); + + checkConsistency(); +} + +int comparePropertyMapEntryIndices(const void* a, const void* b) +{ + unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index; + unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index; + if (ia < ib) + return -1; + if (ia > ib) + return +1; + return 0; +} + +void Structure::getPropertyNames(PropertyNameArray& propertyNames, EnumerationMode mode) +{ + materializePropertyMapIfNecessary(); + if (!m_propertyTable) + return; + + if (m_propertyTable->keyCount < tinyMapThreshold) { + PropertyMapEntry* a[tinyMapThreshold]; + int i = 0; + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned k = 1; k <= entryCount; k++) { + ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[k].attributes & DontEnum)); + if (m_propertyTable->entries()[k].key && (!(m_propertyTable->entries()[k].attributes & DontEnum) || (mode == IncludeDontEnumProperties))) { + PropertyMapEntry* value = &m_propertyTable->entries()[k]; + int j; + for (j = i - 1; j >= 0 && a[j]->index > value->index; --j) + a[j + 1] = a[j]; + a[j + 1] = value; + ++i; + } + } + if (!propertyNames.size()) { + for (int k = 0; k < i; ++k) + propertyNames.addKnownUnique(a[k]->key); + } else { + for (int k = 0; k < i; ++k) + propertyNames.add(a[k]->key); + } + + return; + } + + // Allocate a buffer to use to sort the keys. + Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount); + + // Get pointers to the enumerable entries in the buffer. + PropertyMapEntry** p = sortedEnumerables.data(); + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_propertyTable->entries()[i].key && (!(m_propertyTable->entries()[i].attributes & DontEnum) || (mode == IncludeDontEnumProperties))) + *p++ = &m_propertyTable->entries()[i]; + } + + size_t enumerableCount = p - sortedEnumerables.data(); + // Sort the entries by index. + qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); + sortedEnumerables.resize(enumerableCount); + + // Put the keys of the sorted entries into the list. + if (!propertyNames.size()) { + for (size_t i = 0; i < sortedEnumerables.size(); ++i) + propertyNames.addKnownUnique(sortedEnumerables[i]->key); + } else { + for (size_t i = 0; i < sortedEnumerables.size(); ++i) + propertyNames.add(sortedEnumerables[i]->key); + } +} + +#if DO_PROPERTYMAP_CONSTENCY_CHECK + +void Structure::checkConsistency() +{ + if (!m_propertyTable) + return; + + ASSERT(m_propertyTable->size >= newTableSize); + ASSERT(m_propertyTable->sizeMask); + ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1); + ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask)); + + ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2); + ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4); + + ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2); + + unsigned indexCount = 0; + unsigned deletedIndexCount = 0; + for (unsigned a = 0; a != m_propertyTable->size; ++a) { + unsigned entryIndex = m_propertyTable->entryIndices[a]; + if (entryIndex == emptyEntryIndex) + continue; + if (entryIndex == deletedSentinelIndex) { + ++deletedIndexCount; + continue; + } + ASSERT(entryIndex > deletedSentinelIndex); + ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount); + ++indexCount; + + for (unsigned b = a + 1; b != m_propertyTable->size; ++b) + ASSERT(m_propertyTable->entryIndices[b] != entryIndex); + } + ASSERT(indexCount == m_propertyTable->keyCount); + ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount); + + ASSERT(m_propertyTable->entries()[0].key == 0); + + unsigned nonEmptyEntryCount = 0; + for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) { + ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[c].attributes & DontEnum)); + StringImpl* rep = m_propertyTable->entries()[c].key; + ASSERT(m_propertyTable->entries()[c].offset >= m_anonymousSlotCount); + if (!rep) + continue; + ++nonEmptyEntryCount; + unsigned i = rep->existingHash(); + unsigned k = 0; + unsigned entryIndex; + while (1) { + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + ASSERT(entryIndex != emptyEntryIndex); + if (rep == m_propertyTable->entries()[entryIndex - 1].key) + break; + if (k == 0) + k = 1 | doubleHash(rep->existingHash()); + i += k; + } + ASSERT(entryIndex == c + 1); + } + + ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount); +} + +#endif // DO_PROPERTYMAP_CONSTENCY_CHECK + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h new file mode 100644 index 0000000..f480051 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -0,0 +1,296 @@ +/* + * 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 + * 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 COMPUTER, 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 COMPUTER, 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 Structure_h +#define Structure_h + +#include "Identifier.h" +#include "JSType.h" +#include "JSValue.h" +#include "PropertyMapHashTable.h" +#include "PropertyNameArray.h" +#include "Protect.h" +#include "StructureChain.h" +#include "StructureTransitionTable.h" +#include "JSTypeInfo.h" +#include "UString.h" +#include "WeakGCPtr.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#ifndef NDEBUG +#define DUMP_PROPERTYMAP_STATS 0 +#else +#define DUMP_PROPERTYMAP_STATS 0 +#endif + +namespace JSC { + + class MarkStack; + class PropertyNameArray; + class PropertyNameArrayData; + + enum EnumerationMode { + ExcludeDontEnumProperties, + IncludeDontEnumProperties + }; + + class Structure : public RefCounted<Structure> { + public: + friend class JIT; + friend class StructureTransitionTable; + static PassRefPtr<Structure> create(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount) + { + return adoptRef(new Structure(prototype, typeInfo, anonymousSlotCount)); + } + + static void startIgnoringLeaks(); + static void stopIgnoringLeaks(); + + static void dumpStatistics(); + + static PassRefPtr<Structure> addPropertyTransition(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); + 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> getterSetterTransition(Structure*); + static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*); + static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*); + + PassRefPtr<Structure> flattenDictionaryStructure(JSObject*); + + ~Structure(); + + // 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_dictionaryKind != NoneDictionaryKind; } + bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; } + + const TypeInfo& typeInfo() const { return m_typeInfo; } + + JSValue storedPrototype() const { return m_prototype; } + JSValue prototypeForLookup(ExecState*) const; + StructureChain* prototypeChain(ExecState*) const; + + Structure* previousID() const { return m_previous.get(); } + + void growPropertyStorageCapacity(); + unsigned propertyStorageCapacity() const { return m_propertyStorageCapacity; } + unsigned propertyStorageSize() const { return m_anonymousSlotCount + (m_propertyTable ? m_propertyTable->keyCount + (m_propertyTable->deletedOffsets ? m_propertyTable->deletedOffsets->size() : 0) : static_cast<unsigned>(m_offset + 1)); } + bool isUsingInlineStorage() const; + + size_t get(const Identifier& propertyName); + size_t get(const StringImpl* rep, unsigned& attributes, JSCell*& specificValue); + size_t get(const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue) + { + ASSERT(!propertyName.isNull()); + return get(propertyName.impl(), attributes, specificValue); + } + bool transitionedFor(const JSCell* specificValue) + { + return m_specificValueInPrevious == specificValue; + } + bool hasTransition(StringImpl*, unsigned attributes); + bool hasTransition(const Identifier& propertyName, unsigned attributes) + { + return hasTransition(propertyName.impl(), attributes); + } + + bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } + void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; } + + bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } + + bool hasAnonymousSlots() const { return !!m_anonymousSlotCount; } + unsigned anonymousSlotCount() const { return m_anonymousSlotCount; } + + bool isEmpty() const { return m_propertyTable ? !m_propertyTable->keyCount : m_offset == noOffset; } + + void despecifyDictionaryFunction(const Identifier& propertyName); + void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } + + void setEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. + void clearEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. + JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. + void getPropertyNames(PropertyNameArray&, EnumerationMode mode); + + private: + + Structure(JSValue prototype, const TypeInfo&, unsigned anonymousSlotCount); + + 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 expandPropertyMapHashTable(); + void rehashPropertyMapHashTable(); + void rehashPropertyMapHashTable(unsigned newTableSize); + void createPropertyMapHashTable(); + void createPropertyMapHashTable(unsigned newTableSize); + void insertIntoPropertyMapHashTable(const PropertyMapEntry&); + void checkConsistency(); + + bool despecifyFunction(const Identifier&); + void despecifyAllFunctions(); + + PropertyMapHashTable* copyPropertyTable(); + void materializePropertyMap(); + void materializePropertyMapIfNecessary() + { + if (m_propertyTable || !m_previous) + return; + materializePropertyMap(); + } + + signed char transitionCount() const + { + // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both. + return m_offset == noOffset ? 0 : m_offset + 1; + } + + typedef std::pair<Structure*, Structure*> Transition; + typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable; + + inline bool transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue); + inline void transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue); + inline void transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue); + inline bool transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const; + inline Structure* transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const; + + TransitionTable* transitionTable() const { ASSERT(!m_isUsingSingleSlot); return m_transitions.m_table; } + inline void setTransitionTable(TransitionTable* table); + Structure* singleTransition() const { ASSERT(m_isUsingSingleSlot); return m_transitions.m_singleTransition; } + void setSingleTransition(Structure* structure) { ASSERT(m_isUsingSingleSlot); m_transitions.m_singleTransition = structure; } + + bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; + + static const unsigned emptyEntryIndex = 0; + + static const signed char s_maxTransitionLength = 64; + + static const signed char noOffset = -1; + + static const unsigned maxSpecificFunctionThrashCount = 3; + + TypeInfo m_typeInfo; + + JSValue m_prototype; + mutable RefPtr<StructureChain> m_cachedPrototypeChain; + + RefPtr<Structure> m_previous; + RefPtr<StringImpl> m_nameInPrevious; + JSCell* m_specificValueInPrevious; + + // 'm_isUsingSingleSlot' indicates whether we are using the single transition optimisation. + union { + TransitionTable* m_table; + Structure* m_singleTransition; + } m_transitions; + + WeakGCPtr<JSPropertyNameIterator> m_enumerationCache; + + PropertyMapHashTable* m_propertyTable; + + uint32_t m_propertyStorageCapacity; + + // m_offset does not account for anonymous slots + signed char m_offset; + + unsigned m_dictionaryKind : 2; + bool m_isPinnedPropertyTable : 1; + bool m_hasGetterSetterProperties : 1; + bool m_hasNonEnumerableProperties : 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_specificFunctionThrashCount : 2; + unsigned m_anonymousSlotCount : 5; + unsigned m_isUsingSingleSlot : 1; + // 4 free bits + }; + + inline size_t Structure::get(const Identifier& propertyName) + { + ASSERT(!propertyName.isNull()); + + materializePropertyMapIfNecessary(); + if (!m_propertyTable) + return WTF::notFound; + + StringImpl* rep = propertyName.impl(); + + unsigned i = rep->existingHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return WTF::notFound; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) + return m_propertyTable->entries()[entryIndex - 1].offset; + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | WTF::doubleHash(rep->existingHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return WTF::notFound; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) + return m_propertyTable->entries()[entryIndex - 1].offset; + } + } + +} // namespace JSC + +#endif // Structure_h diff --git a/Source/JavaScriptCore/runtime/StructureChain.cpp b/Source/JavaScriptCore/runtime/StructureChain.cpp new file mode 100644 index 0000000..085876c --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureChain.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 "StructureChain.h" + +#include "JSObject.h" +#include "Structure.h" +#include <wtf/RefPtr.h> + +namespace JSC { + +StructureChain::StructureChain(Structure* head) +{ + size_t size = 0; + for (Structure* current = head; current; current = current->storedPrototype().isNull() ? 0 : asObject(current->storedPrototype())->structure()) + ++size; + + m_vector.set(new RefPtr<Structure>[size + 1]); + + size_t i = 0; + for (Structure* current = head; current; current = current->storedPrototype().isNull() ? 0 : asObject(current->storedPrototype())->structure()) + m_vector[i++] = current; + m_vector[i] = 0; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StructureChain.h b/Source/JavaScriptCore/runtime/StructureChain.h new file mode 100644 index 0000000..816b66d --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureChain.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 StructureChain_h +#define StructureChain_h + +#include <wtf/OwnArrayPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace JSC { + + class Structure; + + class StructureChain : public RefCounted<StructureChain> { + friend class JIT; + + public: + static PassRefPtr<StructureChain> create(Structure* head) { return adoptRef(new StructureChain(head)); } + RefPtr<Structure>* head() { return m_vector.get(); } + + private: + StructureChain(Structure* head); + + OwnArrayPtr<RefPtr<Structure> > m_vector; + }; + +} // namespace JSC + +#endif // StructureChain_h diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h new file mode 100644 index 0000000..7e9d7ff --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -0,0 +1,71 @@ +/* + * 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 + * 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 COMPUTER, 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 COMPUTER, 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 StructureTransitionTable_h +#define StructureTransitionTable_h + +#include "UString.h" +#include <wtf/HashFunctions.h> +#include <wtf/HashMap.h> +#include <wtf/HashTraits.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefPtr.h> + +namespace JSC { + + class Structure; + + struct StructureTransitionTableHash { + typedef std::pair<RefPtr<StringImpl>, unsigned> Key; + static unsigned hash(const Key& p) + { + return p.first->existingHash(); + } + + static bool equal(const Key& a, const Key& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = true; + }; + + struct StructureTransitionTableHashTraits { + typedef WTF::HashTraits<RefPtr<StringImpl> > FirstTraits; + typedef WTF::GenericHashTraits<unsigned> SecondTraits; + typedef std::pair<FirstTraits::TraitType, SecondTraits::TraitType > TraitType; + + 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 || SecondTraits::needsDestruction; + + static void constructDeletedValue(TraitType& slot) { FirstTraits::constructDeletedValue(slot.first); } + static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } + }; + +} // namespace JSC + +#endif // StructureTransitionTable_h diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h new file mode 100644 index 0000000..1b1636d --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolTable.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007, 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 SymbolTable_h +#define SymbolTable_h + +#include "JSObject.h" +#include "UString.h" +#include <wtf/AlwaysInline.h> + +namespace JSC { + + static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); } + + // The bit twiddling in this class assumes that every register index is a + // reasonably small positive or negative number, and therefore has its high + // four bits all set or all unset. + + struct SymbolTableEntry { + SymbolTableEntry() + : m_bits(0) + { + } + + SymbolTableEntry(int index) + { + ASSERT(isValidIndex(index)); + pack(index, false, false); + } + + SymbolTableEntry(int index, unsigned attributes) + { + ASSERT(isValidIndex(index)); + pack(index, attributes & ReadOnly, attributes & DontEnum); + } + + bool isNull() const + { + return !m_bits; + } + + int getIndex() const + { + return m_bits >> FlagBits; + } + + unsigned getAttributes() const + { + unsigned attributes = 0; + if (m_bits & ReadOnlyFlag) + attributes |= ReadOnly; + if (m_bits & DontEnumFlag) + attributes |= DontEnum; + return attributes; + } + + void setAttributes(unsigned attributes) + { + pack(getIndex(), attributes & ReadOnly, attributes & DontEnum); + } + + bool isReadOnly() const + { + return m_bits & ReadOnlyFlag; + } + + private: + static const unsigned ReadOnlyFlag = 0x1; + static const unsigned DontEnumFlag = 0x2; + static const unsigned NotNullFlag = 0x4; + static const unsigned FlagBits = 3; + + void pack(int index, bool readOnly, bool dontEnum) + { + m_bits = (index << FlagBits) | NotNullFlag; + if (readOnly) + m_bits |= ReadOnlyFlag; + if (dontEnum) + m_bits |= DontEnumFlag; + } + + bool isValidIndex(int index) + { + return ((index << FlagBits) >> FlagBits) == index; + } + + int m_bits; + }; + + struct SymbolTableIndexHashTraits { + typedef SymbolTableEntry TraitType; + static SymbolTableEntry emptyValue() { return SymbolTableEntry(); } + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + }; + + typedef HashMap<RefPtr<StringImpl>, SymbolTableEntry, IdentifierRepHash, HashTraits<RefPtr<StringImpl> >, SymbolTableIndexHashTraits> SymbolTable; + + class SharedSymbolTable : public SymbolTable, public RefCounted<SharedSymbolTable> { + public: + static PassRefPtr<SharedSymbolTable> create() { return adoptRef(new SharedSymbolTable); } + private: + SharedSymbolTable() { } + }; + +} // namespace JSC + +#endif // SymbolTable_h diff --git a/Source/JavaScriptCore/runtime/Terminator.h b/Source/JavaScriptCore/runtime/Terminator.h new file mode 100644 index 0000000..6b0f236 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Terminator.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google 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. + * 3. Neither the name of Google Inc. ("Google") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 Terminator_h +#define Terminator_h + +namespace JSC { + +class Terminator { +public: + Terminator() : m_shouldTerminate(false) { } + + void terminateSoon() { m_shouldTerminate = true; } + bool shouldTerminate() const { return m_shouldTerminate; } + +private: + bool m_shouldTerminate; +}; + +} // namespace JSC + +#endif // Terminator_h diff --git a/Source/JavaScriptCore/runtime/TimeoutChecker.cpp b/Source/JavaScriptCore/runtime/TimeoutChecker.cpp new file mode 100644 index 0000000..04d904d --- /dev/null +++ b/Source/JavaScriptCore/runtime/TimeoutChecker.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "TimeoutChecker.h" + +#include "CallFrame.h" +#include "JSGlobalObject.h" + +#if OS(DARWIN) +#include <mach/mach.h> +#elif OS(WINDOWS) +#include <windows.h> +#else +#include "CurrentTime.h" +#endif + +#if PLATFORM(BREWMP) +#include <AEEStdLib.h> +#endif + +using namespace std; + +namespace JSC { + +// Number of ticks before the first timeout check is done. +static const int ticksUntilFirstCheck = 1024; + +// Number of milliseconds between each timeout check. +static const int intervalBetweenChecks = 1000; + +// Returns the time the current thread has spent executing, in milliseconds. +static inline unsigned getCPUTime() +{ +#if OS(DARWIN) + mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT; + thread_basic_info_data_t info; + + // Get thread information + mach_port_t threadPort = mach_thread_self(); + thread_info(threadPort, THREAD_BASIC_INFO, reinterpret_cast<thread_info_t>(&info), &infoCount); + mach_port_deallocate(mach_task_self(), threadPort); + + unsigned time = info.user_time.seconds * 1000 + info.user_time.microseconds / 1000; + time += info.system_time.seconds * 1000 + info.system_time.microseconds / 1000; + + return time; +#elif OS(WINDOWS) + union { + FILETIME fileTime; + unsigned long long fileTimeAsLong; + } userTime, kernelTime; + + // GetThreadTimes won't accept NULL arguments so we pass these even though + // they're not used. + FILETIME creationTime, exitTime; + + GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime.fileTime, &userTime.fileTime); + + return userTime.fileTimeAsLong / 10000 + kernelTime.fileTimeAsLong / 10000; +#elif OS(SYMBIAN) + RThread current; + TTimeIntervalMicroSeconds cpuTime; + + TInt err = current.GetCpuTime(cpuTime); + ASSERT_WITH_MESSAGE(err == KErrNone, "GetCpuTime failed with %d", err); + return cpuTime.Int64() / 1000; +#elif PLATFORM(BREWMP) + // This function returns a continuously and linearly increasing millisecond + // timer from the time the device was powered on. + // There is only one thread in BREW, so this is enough. + return GETUPTIMEMS(); +#else + // FIXME: We should return the time the current thread has spent executing. + + // use a relative time from first call in order to avoid an overflow + static double firstTime = currentTime(); + return (currentTime() - firstTime) * 1000; +#endif +} + +TimeoutChecker::TimeoutChecker() + : m_timeoutInterval(0) + , m_startCount(0) +{ + reset(); +} + +void TimeoutChecker::reset() +{ + m_ticksUntilNextCheck = ticksUntilFirstCheck; + m_timeAtLastCheck = 0; + m_timeExecuting = 0; +} + +bool TimeoutChecker::didTimeOut(ExecState* exec) +{ + unsigned currentTime = getCPUTime(); + + if (!m_timeAtLastCheck) { + // Suspicious amount of looping in a script -- start timing it + m_timeAtLastCheck = currentTime; + return false; + } + + unsigned timeDiff = currentTime - m_timeAtLastCheck; + + if (timeDiff == 0) + timeDiff = 1; + + m_timeExecuting += timeDiff; + m_timeAtLastCheck = currentTime; + + // Adjust the tick threshold so we get the next checkTimeout call in the + // interval specified in intervalBetweenChecks. + m_ticksUntilNextCheck = static_cast<unsigned>((static_cast<float>(intervalBetweenChecks) / timeDiff) * m_ticksUntilNextCheck); + // If the new threshold is 0 reset it to the default threshold. This can happen if the timeDiff is higher than the + // preferred script check time interval. + if (m_ticksUntilNextCheck == 0) + m_ticksUntilNextCheck = ticksUntilFirstCheck; + + if (m_timeoutInterval && m_timeExecuting > m_timeoutInterval) { + if (exec->dynamicGlobalObject()->shouldInterruptScript()) + return true; + + reset(); + } + + return false; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/TimeoutChecker.h b/Source/JavaScriptCore/runtime/TimeoutChecker.h new file mode 100644 index 0000000..71ce169 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TimeoutChecker.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 TimeoutChecker_h +#define TimeoutChecker_h + +#include <wtf/Assertions.h> + +#ifdef ANDROID_INSTRUMENT +#include "TimeCounter.h" +#endif + +namespace JSC { + + class ExecState; + + class TimeoutChecker { + public: + TimeoutChecker(); + + void setTimeoutInterval(unsigned timeoutInterval) { m_timeoutInterval = timeoutInterval; } + unsigned timeoutInterval() const { return m_timeoutInterval; } + + unsigned ticksUntilNextCheck() { return m_ticksUntilNextCheck; } + + void start() + { + if (!m_startCount) + reset(); +#ifdef ANDROID_INSTRUMENT + if (!m_startCount) + android::TimeCounter::start(android::TimeCounter::JavaScriptTimeCounter); +#endif + ++m_startCount; + } + + void stop() + { + ASSERT(m_startCount); + --m_startCount; +#ifdef ANDROID_INSTRUMENT + if (!m_startCount) + android::TimeCounter::record(android::TimeCounter::JavaScriptTimeCounter, __FUNCTION__); +#endif + } + + void reset(); + + bool didTimeOut(ExecState*); + + private: + unsigned m_timeoutInterval; + unsigned m_timeAtLastCheck; + unsigned m_timeExecuting; + unsigned m_startCount; + unsigned m_ticksUntilNextCheck; + }; + +} // namespace JSC + +#endif // TimeoutChecker_h diff --git a/Source/JavaScriptCore/runtime/Tracing.d b/Source/JavaScriptCore/runtime/Tracing.d new file mode 100644 index 0000000..da854b9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Tracing.d @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 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. + */ + +provider JavaScriptCore +{ + probe gc__begin(); + probe gc__marked(); + probe gc__end(); + + probe profile__will_execute(int, char*, char*, int); + probe profile__did_execute(int, char*, char*, int); +}; + +#pragma D attributes Unstable/Unstable/Common provider JavaScriptCore provider +#pragma D attributes Private/Private/Unknown provider JavaScriptCore module +#pragma D attributes Private/Private/Unknown provider JavaScriptCore function +#pragma D attributes Unstable/Unstable/Common provider JavaScriptCore name +#pragma D attributes Unstable/Unstable/Common provider JavaScriptCore args diff --git a/Source/JavaScriptCore/runtime/Tracing.h b/Source/JavaScriptCore/runtime/Tracing.h new file mode 100644 index 0000000..c28c85f --- /dev/null +++ b/Source/JavaScriptCore/runtime/Tracing.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 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 Tracing_h +#define Tracing_h + +#if HAVE(DTRACE) +#include "TracingDtrace.h" +#else + +#define JAVASCRIPTCORE_GC_BEGIN() +#define JAVASCRIPTCORE_GC_BEGIN_ENABLED() 0 + +#define JAVASCRIPTCORE_GC_END() +#define JAVASCRIPTCORE_GC_END_ENABLED() 0 + +#define JAVASCRIPTCORE_GC_MARKED() +#define JAVASCRIPTCORE_GC_MARKED_ENABLED() 0 + +#define JAVASCRIPTCORE_PROFILE_WILL_EXECUTE(arg0, arg1, arg2, arg3) +#define JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED() 0 + +#define JAVASCRIPTCORE_PROFILE_DID_EXECUTE(arg0, arg1, arg2, arg3) +#define JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED() 0 + +#endif + +#endif // Tracing_h diff --git a/Source/JavaScriptCore/runtime/UString.cpp b/Source/JavaScriptCore/runtime/UString.cpp new file mode 100644 index 0000000..b3cd40c --- /dev/null +++ b/Source/JavaScriptCore/runtime/UString.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2009 Google 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 + * 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 "UString.h" + +#include "JSGlobalObjectFunctions.h" +#include "Collector.h" +#include "Identifier.h" +#include "Operations.h" +#include <ctype.h> +#include <limits.h> +#include <limits> +#include <stdio.h> +#include <stdlib.h> +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> +#include <wtf/DecimalNumber.h> +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/Vector.h> +#include <wtf/unicode/UTF8.h> + +#if HAVE(STRINGS_H) +#include <strings.h> +#endif + +using namespace WTF; +using namespace WTF::Unicode; +using namespace std; + +namespace JSC { + +extern const double NaN; +extern const double Inf; + +COMPILE_ASSERT(sizeof(UString) == sizeof(void*), UString_should_stay_small); + +// Construct a string with UTF-16 data. +UString::UString(const UChar* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) +{ +} + +// Construct a string with UTF-16 data, from a null-terminated source. +UString::UString(const UChar* characters) +{ + if (!characters) + return; + + int length = 0; + while (characters[length] != UChar(0)) + ++length; + + m_impl = StringImpl::create(characters, length); +} + +// Construct a string with latin1 data. +UString::UString(const char* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) +{ +} + +// Construct a string with latin1 data, from a null-terminated source. +UString::UString(const char* characters) + : m_impl(characters ? StringImpl::create(characters) : 0) +{ +} + +UString UString::number(int i) +{ + UChar buf[1 + sizeof(i) * 3]; + UChar* end = buf + WTF_ARRAY_LENGTH(buf); + UChar* p = end; + + if (i == 0) + *--p = '0'; + else if (i == INT_MIN) { + char minBuf[1 + sizeof(i) * 3]; + snprintf(minBuf, sizeof(minBuf), "%d", INT_MIN); + 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<unsigned>(end - p)); +} + +UString UString::number(long long i) +{ + UChar buf[1 + sizeof(i) * 3]; + UChar* end = buf + WTF_ARRAY_LENGTH(buf); + UChar* p = end; + + if (i == 0) + *--p = '0'; + else if (i == std::numeric_limits<long long>::min()) { + char minBuf[1 + sizeof(i) * 3]; +#if OS(WINDOWS) + snprintf(minBuf, sizeof(minBuf), "%I64d", std::numeric_limits<long long>::min()); +#else + snprintf(minBuf, sizeof(minBuf), "%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<unsigned>(end - p)); +} + +UString UString::number(unsigned u) +{ + UChar buf[sizeof(u) * 3]; + UChar* end = buf + WTF_ARRAY_LENGTH(buf); + UChar* p = end; + + if (u == 0) + *--p = '0'; + else { + while (u) { + *--p = static_cast<unsigned short>((u % 10) + '0'); + u /= 10; + } + } + + return UString(p, static_cast<unsigned>(end - p)); +} + +UString UString::number(long l) +{ + UChar buf[1 + sizeof(l) * 3]; + UChar* end = buf + WTF_ARRAY_LENGTH(buf); + UChar* p = end; + + if (l == 0) + *--p = '0'; + else if (l == LONG_MIN) { + char minBuf[1 + sizeof(l) * 3]; + snprintf(minBuf, sizeof(minBuf), "%ld", LONG_MIN); + return UString(minBuf); + } else { + bool negative = false; + if (l < 0) { + negative = true; + l = -l; + } + while (l) { + *--p = static_cast<unsigned short>((l % 10) + '0'); + l /= 10; + } + if (negative) + *--p = '-'; + } + + return UString(p, end - p); +} + +UString UString::number(double d) +{ + NumberToStringBuffer buffer; + unsigned length = numberToString(d, buffer); + return UString(buffer, length); +} + +UString UString::substringSharingImpl(unsigned offset, unsigned length) const +{ + // FIXME: We used to check against a limit of Heap::minExtraCost / sizeof(UChar). + + unsigned stringLength = this->length(); + offset = min(offset, stringLength); + length = min(length, stringLength - offset); + + if (!offset && length == stringLength) + return *this; + return UString(StringImpl::create(m_impl, offset, length)); +} + +bool operator==(const UString& s1, const char *s2) +{ + if (s2 == 0) + return s1.isEmpty(); + + const UChar* u = s1.characters(); + const UChar* uend = u + s1.length(); + while (u != uend && *s2) { + if (u[0] != (unsigned char)*s2) + return false; + s2++; + u++; + } + + return u == uend && *s2 == 0; +} + +bool operator<(const UString& s1, const UString& s2) +{ + const unsigned l1 = s1.length(); + const unsigned l2 = s2.length(); + const unsigned lmin = l1 < l2 ? l1 : l2; + const UChar* c1 = s1.characters(); + const UChar* c2 = s2.characters(); + unsigned l = 0; + while (l < lmin && *c1 == *c2) { + c1++; + c2++; + l++; + } + if (l < lmin) + return (c1[0] < c2[0]); + + return (l1 < l2); +} + +bool operator>(const UString& s1, const UString& s2) +{ + const unsigned l1 = s1.length(); + const unsigned l2 = s2.length(); + const unsigned lmin = l1 < l2 ? l1 : l2; + const UChar* c1 = s1.characters(); + const UChar* c2 = s2.characters(); + unsigned l = 0; + while (l < lmin && *c1 == *c2) { + c1++; + c2++; + l++; + } + if (l < lmin) + return (c1[0] > c2[0]); + + return (l1 > l2); +} + +CString UString::ascii() const +{ + // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are + // preserved, characters outside of this range are converted to '?'. + + unsigned length = this->length(); + const UChar* characters = this->characters(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch >= 0x7f) ? '?' : ch; + } + + return result; +} + +CString UString::latin1() const +{ + // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are + // preserved, characters outside of this range are converted to '?'. + + unsigned length = this->length(); + const UChar* characters = this->characters(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch > 0xff ? '?' : ch; + } + + return result; +} + +// Helper to write a three-byte UTF-8 code point to the buffer, caller must check room is available. +static inline void putUTF8Triple(char*& buffer, UChar ch) +{ + ASSERT(ch >= 0x0800); + *buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0); + *buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80); + *buffer++ = static_cast<char>((ch & 0x3F) | 0x80); +} + +CString UString::utf8(bool strict) const +{ + unsigned length = this->length(); + const UChar* characters = this->characters(); + + // Allocate a buffer big enough to hold all the characters + // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). + // Optimization ideas, if we find this function is hot: + // * We could speculatively create a CStringBuffer to contain 'length' + // characters, and resize if necessary (i.e. if the buffer contains + // non-ascii characters). (Alternatively, scan the buffer first for + // ascii characters, so we know this will be sufficient). + // * We could allocate a CStringBuffer with an appropriate size to + // have a good chance of being able to write the string into the + // buffer without reallocing (say, 1.5 x length). + if (length > numeric_limits<unsigned>::max() / 3) + return CString(); + Vector<char, 1024> bufferVector(length * 3); + + char* buffer = bufferVector.data(); + ConversionResult result = convertUTF16ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size(), strict); + ASSERT(result != targetExhausted); // (length * 3) should be sufficient for any conversion + + // Only produced from strict conversion. + if (result == sourceIllegal) + return CString(); + + // Check for an unconverted high surrogate. + if (result == sourceExhausted) { + if (strict) + return CString(); + // This should be one unpaired high surrogate. Treat it the same + // was as an unpaired high surrogate would have been handled in + // the middle of a string with non-strict conversion - which is + // to say, simply encode it to UTF-8. + ASSERT((characters + 1) == (this->characters() + length)); + ASSERT((*characters >= 0xD800) && (*characters <= 0xDBFF)); + // There should be room left, since one UChar hasn't been converted. + ASSERT((buffer + 3) <= (buffer + bufferVector.size())); + putUTF8Triple(buffer, *characters); + } + + return CString(bufferVector.data(), buffer - bufferVector.data()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/UString.h b/Source/JavaScriptCore/runtime/UString.h new file mode 100644 index 0000000..8f6c083 --- /dev/null +++ b/Source/JavaScriptCore/runtime/UString.h @@ -0,0 +1,263 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google 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 + * 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. + * + */ + +#ifndef UString_h +#define UString_h + +#include <wtf/text/StringImpl.h> + +namespace JSC { + +class UString { +public: + // Construct a null string, distinguishable from an empty string. + UString() { } + + // Construct a string with UTF-16 data. + UString(const UChar* characters, unsigned length); + + // Construct a string with UTF-16 data, from a null-terminated source. + UString(const UChar*); + + // Construct a string with latin1 data. + UString(const char* characters, unsigned length); + + // Construct a string with latin1 data, from a null-terminated source. + UString(const char* characters); + + // Construct a string referencing an existing StringImpl. + UString(StringImpl* impl) : m_impl(impl) { } + UString(PassRefPtr<StringImpl> impl) : m_impl(impl) { } + UString(RefPtr<StringImpl> impl) : m_impl(impl) { } + + // Inline the destructor. + ALWAYS_INLINE ~UString() { } + + void swap(UString& o) { m_impl.swap(o.m_impl); } + + template<size_t inlineCapacity> + static UString adopt(Vector<UChar, inlineCapacity>& vector) { return StringImpl::adopt(vector); } + + bool isNull() const { return !m_impl; } + bool isEmpty() const { return !m_impl || !m_impl->length(); } + + StringImpl* impl() const { return m_impl.get(); } + + unsigned length() const + { + if (!m_impl) + return 0; + return m_impl->length(); + } + + const UChar* characters() const + { + if (!m_impl) + return 0; + return m_impl->characters(); + } + + CString ascii() const; + CString latin1() const; + CString utf8(bool strict = false) const; + + UChar operator[](unsigned index) const + { + if (!m_impl || index >= m_impl->length()) + return 0; + return m_impl->characters()[index]; + } + + static UString number(int); + static UString number(unsigned); + static UString number(long); + static UString number(long long); + static UString number(double); + + // Find a single character or string, also with match function & latin1 forms. + size_t find(UChar c, unsigned start = 0) const + { return m_impl ? m_impl->find(c, start) : notFound; } + size_t find(const UString& str, unsigned start = 0) const + { return m_impl ? m_impl->find(str.impl(), start) : notFound; } + size_t find(const char* str, unsigned start = 0) const + { return m_impl ? m_impl->find(str, start) : notFound; } + + // Find the last instance of a single character or string. + size_t reverseFind(UChar c, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFind(c, start) : notFound; } + size_t reverseFind(const UString& str, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFind(str.impl(), start) : notFound; } + + UString substringSharingImpl(unsigned pos, unsigned len = UINT_MAX) const; + +private: + RefPtr<StringImpl> m_impl; +}; + +ALWAYS_INLINE bool operator==(const UString& s1, const UString& s2) +{ + StringImpl* rep1 = s1.impl(); + StringImpl* rep2 = s2.impl(); + unsigned size1 = 0; + unsigned size2 = 0; + + if (rep1 == rep2) // If they're the same rep, they're equal. + return true; + + if (rep1) + size1 = rep1->length(); + + if (rep2) + size2 = rep2->length(); + + if (size1 != size2) // If the lengths are not the same, we're done. + return false; + + if (!size1) + return true; + + // At this point we know + // (a) that the strings are the same length and + // (b) that they are greater than zero length. + const UChar* d1 = rep1->characters(); + const UChar* d2 = rep2->characters(); + + if (d1 == d2) // Check to see if the data pointers are the same. + return true; + + // Do quick checks for sizes 1 and 2. + switch (size1) { + case 1: + return d1[0] == d2[0]; + case 2: + return (d1[0] == d2[0]) & (d1[1] == d2[1]); + default: + return memcmp(d1, d2, size1 * sizeof(UChar)) == 0; + } +} + + +inline bool operator!=(const UString& s1, const UString& s2) +{ + return !JSC::operator==(s1, s2); +} + +bool operator<(const UString& s1, const UString& s2); +bool operator>(const UString& s1, const UString& s2); + +bool operator==(const UString& s1, const char* s2); + +inline bool operator!=(const UString& s1, const char* s2) +{ + return !JSC::operator==(s1, s2); +} + +inline bool operator==(const char *s1, const UString& s2) +{ + return operator==(s2, s1); +} + +inline bool operator!=(const char *s1, const UString& s2) +{ + return !JSC::operator==(s1, s2); +} + +inline int codePointCompare(const UString& s1, const UString& s2) +{ + return codePointCompare(s1.impl(), s2.impl()); +} + +struct UStringHash { + static unsigned hash(StringImpl* key) { return key->hash(); } + static bool equal(const StringImpl* a, const StringImpl* b) + { + if (a == b) + return true; + if (!a || !b) + return false; + + unsigned aLength = a->length(); + unsigned bLength = b->length(); + if (aLength != bLength) + return false; + + // FIXME: perhaps we should have a more abstract macro that indicates when + // going 4 bytes at a time is unsafe +#if CPU(ARM) || CPU(SH4) || CPU(MIPS) + const UChar* aChars = a->characters(); + const UChar* bChars = b->characters(); + for (unsigned i = 0; i != aLength; ++i) { + if (*aChars++ != *bChars++) + return false; + } + return true; +#else + /* Do it 4-bytes-at-a-time on architectures where it's safe */ + const uint32_t* aChars = reinterpret_cast<const uint32_t*>(a->characters()); + const uint32_t* bChars = reinterpret_cast<const uint32_t*>(b->characters()); + + unsigned halfLength = aLength >> 1; + for (unsigned i = 0; i != halfLength; ++i) + if (*aChars++ != *bChars++) + return false; + + if (aLength & 1 && *reinterpret_cast<const uint16_t*>(aChars) != *reinterpret_cast<const uint16_t*>(bChars)) + return false; + + return true; +#endif + } + + static unsigned hash(const RefPtr<StringImpl>& key) { return key->hash(); } + static bool equal(const RefPtr<StringImpl>& a, const RefPtr<StringImpl>& b) + { + return equal(a.get(), b.get()); + } + + static unsigned hash(const UString& key) { return key.impl()->hash(); } + static bool equal(const UString& a, const UString& b) + { + return equal(a.impl(), b.impl()); + } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +} // namespace JSC + +namespace WTF { + +// UStringHash is the default hash for UString +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::UString> { + typedef JSC::UStringHash Hash; +}; + +template <> struct VectorTraits<JSC::UString> : SimpleClassVectorTraits +{ + static const bool canInitializeWithMemset = true; +}; + +} // namespace WTF + +#endif + diff --git a/Source/JavaScriptCore/runtime/UStringBuilder.h b/Source/JavaScriptCore/runtime/UStringBuilder.h new file mode 100644 index 0000000..31ccf38 --- /dev/null +++ b/Source/JavaScriptCore/runtime/UStringBuilder.h @@ -0,0 +1,43 @@ +/* + * 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 UStringBuilder_h +#define UStringBuilder_h + +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +class UStringBuilder : public StringBuilder { +public: + using StringBuilder::append; + void append(const UString& str) { append(String(str.impl())); } + + UString toUString() { return toString().impl(); } +}; + +} // namespace JSC + +#endif // UStringBuilder_h diff --git a/Source/JavaScriptCore/runtime/UStringConcatenate.h b/Source/JavaScriptCore/runtime/UStringConcatenate.h new file mode 100644 index 0000000..0990c72 --- /dev/null +++ b/Source/JavaScriptCore/runtime/UStringConcatenate.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 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 UStringConcatenate_h +#define UStringConcatenate_h + +#include "UString.h" +#include <wtf/text/StringConcatenate.h> + +namespace WTF { + +template<> +class StringTypeAdapter<JSC::UString> { +public: + StringTypeAdapter<JSC::UString>(JSC::UString& string) + : m_data(string.characters()) + , m_length(string.length()) + { + } + + unsigned length() { return m_length; } + + void writeTo(UChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) + destination[i] = m_data[i]; + } + +private: + const UChar* m_data; + unsigned m_length; +}; + +}; // namespace WTF + +namespace JSC { + +template<typename StringType1, typename StringType2> +UString makeUString(StringType1 string1, StringType2 string2) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5, string6); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5, string6, string7); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5, string6, string7, string8); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +} // namespace JSC + +#endif diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h new file mode 100644 index 0000000..6b96a74 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakGCMap.h @@ -0,0 +1,123 @@ +/* + * 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. AND ITS CONTRIBUTORS ``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 ITS 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 WeakGCMap_h +#define WeakGCMap_h + +#include "Collector.h" +#include <wtf/HashMap.h> + +namespace JSC { + +class JSCell; + +// A HashMap whose get() function returns emptyValue() for cells awaiting destruction. +template<typename KeyType, typename MappedType> +class WeakGCMap : public FastAllocBase { + /* + Invariants: + * A value enters the WeakGCMap marked. (Guaranteed by set().) + * A value that becomes unmarked leaves the WeakGCMap before being recycled. (Guaranteed by the value's destructor removing it from the WeakGCMap.) + * A value that becomes unmarked leaves the WeakGCMap before becoming marked again. (Guaranteed by all destructors running before the mark phase begins.) + * During the mark phase, all values in the WeakGCMap are valid. (Guaranteed by all destructors running before the mark phase begins.) + */ + +public: + typedef typename HashMap<KeyType, MappedType>::iterator iterator; + typedef typename HashMap<KeyType, MappedType>::const_iterator const_iterator; + + bool isEmpty() { return m_map.isEmpty(); } + void clear() { m_map.clear(); } + + MappedType get(const KeyType& key) const; + pair<iterator, bool> set(const KeyType&, const MappedType&); + MappedType take(const KeyType& key); + + // These unchecked functions provide access to a value even if the value's + // mark bit is not set. This is used, among other things, to retrieve values + // during the GC mark phase, which begins by clearing all mark bits. + + MappedType uncheckedGet(const KeyType& key) const { return m_map.get(key); } + bool uncheckedRemove(const KeyType&, const MappedType&); + + iterator uncheckedBegin() { return m_map.begin(); } + iterator uncheckedEnd() { return m_map.end(); } + + const_iterator uncheckedBegin() const { return m_map.begin(); } + const_iterator uncheckedEnd() const { return m_map.end(); } + +private: + HashMap<KeyType, MappedType> m_map; +}; + +template<typename KeyType, typename MappedType> +inline MappedType WeakGCMap<KeyType, MappedType>::get(const KeyType& key) const +{ + MappedType result = m_map.get(key); + if (result == HashTraits<MappedType>::emptyValue()) + return result; + if (!Heap::isCellMarked(result)) + return HashTraits<MappedType>::emptyValue(); + return result; +} + +template<typename KeyType, typename MappedType> +MappedType WeakGCMap<KeyType, MappedType>::take(const KeyType& key) +{ + MappedType result = m_map.take(key); + if (result == HashTraits<MappedType>::emptyValue()) + return result; + if (!Heap::isCellMarked(result)) + return HashTraits<MappedType>::emptyValue(); + return result; +} + +template<typename KeyType, typename MappedType> +pair<typename HashMap<KeyType, MappedType>::iterator, bool> WeakGCMap<KeyType, MappedType>::set(const KeyType& key, const MappedType& value) +{ + Heap::markCell(value); // If value is newly allocated, it's not marked, so mark it now. + pair<iterator, bool> result = m_map.add(key, value); + if (!result.second) { // pre-existing entry + result.second = !Heap::isCellMarked(result.first->second); + result.first->second = value; + } + return result; +} + +template<typename KeyType, typename MappedType> +bool WeakGCMap<KeyType, MappedType>::uncheckedRemove(const KeyType& key, const MappedType& value) +{ + iterator it = m_map.find(key); + if (it == m_map.end()) + return false; + if (it->second != value) + return false; + m_map.remove(it); + return true; +} + +} // namespace JSC + +#endif // WeakGCMap_h diff --git a/Source/JavaScriptCore/runtime/WeakGCPtr.h b/Source/JavaScriptCore/runtime/WeakGCPtr.h new file mode 100644 index 0000000..ac77cf3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakGCPtr.h @@ -0,0 +1,153 @@ +/* + * 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. AND ITS CONTRIBUTORS ``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 ITS 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 WeakGCPtr_h +#define WeakGCPtr_h + +#include "Collector.h" +#include "GCHandle.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +// A smart pointer whose get() function returns 0 for cells awaiting destruction. +template <typename T> class WeakGCPtr : Noncopyable { +public: + WeakGCPtr() + : m_ptr(0) + { + } + + WeakGCPtr(T* ptr) { assign(ptr); } + + ~WeakGCPtr() + { + if (m_ptr) + m_ptr->pool()->free(m_ptr); + } + + T* get() const + { + if (m_ptr && m_ptr->isValidPtr()) + return static_cast<T*>(m_ptr->get()); + return 0; + } + + bool clear(JSCell* p) + { + if (!m_ptr || m_ptr->get() != p) + return false; + + m_ptr->pool()->free(m_ptr); + m_ptr = 0; + return true; + } + + T& operator*() const { return *get(); } + T* operator->() const { return get(); } + + bool operator!() const { return !get(); } + + // This conversion operator allows implicit conversion to bool but not to other integer types. +#if COMPILER(WINSCW) + operator bool() const { return m_ptr; } +#else + typedef WeakGCHandle* WeakGCPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return get() ? &WeakGCPtr::m_ptr : 0; } +#endif + + WeakGCPtr& operator=(T*); + +#if !ASSERT_DISABLED + bool hasDeadObject() const { return !!m_ptr; } +#endif + +private: + void assign(JSCell* ptr) + { + ASSERT(ptr); + if (m_ptr) + m_ptr->set(ptr); + else + m_ptr = Heap::heap(ptr)->addWeakGCHandle(ptr); + } + + WeakGCHandle* m_ptr; +}; + +template <typename T> inline WeakGCPtr<T>& WeakGCPtr<T>::operator=(T* optr) +{ + assign(optr); + return *this; +} + +template <typename T, typename U> inline bool operator==(const WeakGCPtr<T>& a, const WeakGCPtr<U>& b) +{ + return a.get() == b.get(); +} + +template <typename T, typename U> inline bool operator==(const WeakGCPtr<T>& a, U* b) +{ + return a.get() == b; +} + +template <typename T, typename U> inline bool operator==(T* a, const WeakGCPtr<U>& b) +{ + return a == b.get(); +} + +template <typename T, typename U> inline bool operator!=(const WeakGCPtr<T>& a, const WeakGCPtr<U>& b) +{ + return a.get() != b.get(); +} + +template <typename T, typename U> inline bool operator!=(const WeakGCPtr<T>& a, U* b) +{ + return a.get() != b; +} + +template <typename T, typename U> inline bool operator!=(T* a, const WeakGCPtr<U>& b) +{ + return a != b.get(); +} + +template <typename T, typename U> inline WeakGCPtr<T> static_pointer_cast(const WeakGCPtr<U>& p) +{ + return WeakGCPtr<T>(static_cast<T*>(p.get())); +} + +template <typename T, typename U> inline WeakGCPtr<T> const_pointer_cast(const WeakGCPtr<U>& p) +{ + return WeakGCPtr<T>(const_cast<T*>(p.get())); +} + +template <typename T> inline T* get(const WeakGCPtr<T>& p) +{ + return p.get(); +} + +} // namespace JSC + +#endif // WeakGCPtr_h diff --git a/Source/JavaScriptCore/runtime/WeakRandom.h b/Source/JavaScriptCore/runtime/WeakRandom.h new file mode 100644 index 0000000..ff3995e --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakRandom.h @@ -0,0 +1,86 @@ +/* + * 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. + * + * + * Copyright (c) 2009 Ian C. Bullard + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef WeakRandom_h +#define WeakRandom_h + +#include <limits.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +class WeakRandom { +public: + WeakRandom(unsigned seed) + : m_low(seed ^ 0x49616E42) + , m_high(seed) + { + } + + double get() + { + return advance() / (UINT_MAX + 1.0); + } + +private: + unsigned advance() + { + m_high = (m_high << 16) + (m_high >> 16); + m_high += m_low; + m_low += m_high; + return m_high; + } + + unsigned m_low; + unsigned m_high; +}; + +} // namespace JSC + +#endif // WeakRandom_h |