diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/JavaScriptCore/runtime | |
download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/JavaScriptCore/runtime')
199 files changed, 38293 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/ArgList.cpp b/Source/JavaScriptCore/runtime/ArgList.cpp new file mode 100644 index 000000000..873ddc2da --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgList.cpp @@ -0,0 +1,89 @@ +/* + * 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 "HeapRootVisitor.h" +#include "JSValue.h" +#include "JSObject.h" +#include "ScopeChain.h" + +using std::min; + +namespace JSC { + +void ArgList::getSlice(int startIndex, ArgList& result) const +{ + if (startIndex <= 0 || startIndex >= m_argCount) { + result = ArgList(); + return; + } + + result.m_args = m_args - startIndex; + result.m_argCount = m_argCount - startIndex; +} + +void MarkedArgumentBuffer::markLists(HeapRootVisitor& heapRootVisitor, ListSet& markSet) +{ + ListSet::iterator end = markSet.end(); + for (ListSet::iterator it = markSet.begin(); it != end; ++it) { + MarkedArgumentBuffer* list = *it; + for (int i = 0; i < list->m_size; ++i) + heapRootVisitor.visit(reinterpret_cast<JSValue*>(&list->slotFor(i))); + } +} + +void MarkedArgumentBuffer::slowAppend(JSValue v) +{ + int newCapacity = m_capacity * 4; + EncodedJSValue* newBuffer = &(new EncodedJSValue[newCapacity])[newCapacity - 1]; + for (int i = 0; i < m_capacity; ++i) + newBuffer[-i] = m_buffer[-i]; + + if (EncodedJSValue* base = mallocBase()) + delete [] base; + + m_buffer = newBuffer; + m_capacity = newCapacity; + + slotFor(m_size) = JSValue::encode(v); + ++m_size; + + if (m_markSet) + return; + + // 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. + for (int i = 0; i < m_size; ++i) { + Heap* heap = Heap::heap(JSValue::decode(slotFor(i))); + if (!heap) + continue; + + m_markSet = &heap->markListSet(); + m_markSet->add(this); + break; + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArgList.h b/Source/JavaScriptCore/runtime/ArgList.h new file mode 100644 index 000000000..ac5a4f271 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgList.h @@ -0,0 +1,183 @@ +/* + * 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 "WriteBarrier.h" +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +namespace JSC { + + class SlotVisitor; + + class MarkedArgumentBuffer { + WTF_MAKE_NONCOPYABLE(MarkedArgumentBuffer); + friend class JSGlobalData; + friend class ArgList; + + private: + static const size_t inlineCapacity = 8; + typedef Vector<Register, inlineCapacity> VectorType; + typedef HashSet<MarkedArgumentBuffer*> ListSet; + + public: + // 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_size(0) + , m_capacity(inlineCapacity) + , m_buffer(&m_inlineBuffer[m_capacity - 1]) + , m_markSet(0) + { + } + + ~MarkedArgumentBuffer() + { + if (m_markSet) + m_markSet->remove(this); + + if (EncodedJSValue* base = mallocBase()) + delete [] base; + } + + size_t size() const { return m_size; } + bool isEmpty() const { return !m_size; } + + JSValue at(int i) const + { + if (i >= m_size) + return jsUndefined(); + + return JSValue::decode(slotFor(i)); + } + + void clear() + { + m_size = 0; + } + + void append(JSValue v) + { + if (m_size >= m_capacity) + return slowAppend(v); + + slotFor(m_size) = JSValue::encode(v); + ++m_size; + } + + void removeLast() + { + ASSERT(m_size); + m_size--; + } + + JSValue last() + { + ASSERT(m_size); + return JSValue::decode(slotFor(m_size - 1)); + } + + static void markLists(HeapRootVisitor&, ListSet&); + + private: + void slowAppend(JSValue); + + EncodedJSValue& slotFor(int item) const + { + return m_buffer[-item]; + } + + EncodedJSValue* mallocBase() + { + if (m_capacity == static_cast<int>(inlineCapacity)) + return 0; + return &slotFor(m_capacity - 1); + } + + int m_size; + int m_capacity; + EncodedJSValue m_inlineBuffer[inlineCapacity]; + EncodedJSValue* m_buffer; + ListSet* m_markSet; + + private: + // Prohibits new / delete, which would break GC. + 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: + ArgList() + : m_args(0) + , m_argCount(0) + { + } + + ArgList(ExecState* exec) + : m_args(reinterpret_cast<JSValue*>(&exec[CallFrame::argumentOffset(0)])) + , m_argCount(exec->argumentCount()) + { + } + + ArgList(const MarkedArgumentBuffer& args) + : m_args(reinterpret_cast<JSValue*>(args.m_buffer)) + , m_argCount(args.size()) + { + } + + JSValue at(int i) const + { + if (i >= m_argCount) + return jsUndefined(); + return m_args[-i]; + } + + bool isEmpty() const { return !m_argCount; } + size_t size() const { return m_argCount; } + + void getSlice(int startIndex, ArgList& result) const; + + private: + JSValue* m_args; + int 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 000000000..fc136eb7e --- /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::s_info = { "Arguments", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(Arguments) }; + +void Arguments::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + JSObject::visitChildren(thisObject, visitor); + + if (thisObject->d->registerArray) + visitor.appendValues(thisObject->d->registerArray.get(), thisObject->d->numArguments); + visitor.append(&thisObject->d->callee); + if (thisObject->d->activation) + visitor.append(&thisObject->d->activation); +} + +void Arguments::destroy(JSCell* cell) +{ + jsCast<Arguments*>(cell)->Arguments::~Arguments(); +} + +void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) +{ + if (UNLIKELY(d->overrodeLength)) { + length = min(get(exec, exec->propertyNames().length).toUInt32(exec), length); + for (unsigned i = 0; i < length; i++) + callFrame->setArgument(i, get(exec, i)); + return; + } + ASSERT(length == this->length(exec)); + for (size_t i = 0; i < length; ++i) { + if (!d->deletedArguments || !d->deletedArguments[i]) + callFrame->setArgument(i, argument(i).get()); + else + callFrame->setArgument(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; + } + uint32_t length = this->length(exec); + for (size_t i = 0; i < length; ++i) { + if (!d->deletedArguments || !d->deletedArguments[i]) + args.append(argument(i).get()); + else + args.append(get(exec, i)); + } +} + +bool Arguments::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + if (i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + slot.setValue(thisObject->argument(i).get()); + return true; + } + + return JSObject::getOwnPropertySlot(thisObject, exec, Identifier(exec, UString::number(i)), slot); +} + +void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) +{ + if (d->overrodeCaller) + return; + + d->overrodeCaller = true; + PropertyDescriptor descriptor; + descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec), DontEnum | DontDelete | Getter | Setter); + methodTable()->defineOwnProperty(this, exec, exec->propertyNames().caller, descriptor, false); +} + +void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec) +{ + if (d->overrodeCallee) + return; + + d->overrodeCallee = true; + PropertyDescriptor descriptor; + descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(exec), DontEnum | DontDelete | Getter | Setter); + methodTable()->defineOwnProperty(this, exec, exec->propertyNames().callee, descriptor, false); +} + +bool Arguments::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + slot.setValue(thisObject->argument(i).get()); + return true; + } + + if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->d->overrodeLength)) { + slot.setValue(jsNumber(thisObject->d->numArguments)); + return true; + } + + if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->d->overrodeCallee)) { + if (!thisObject->d->isStrictMode) { + slot.setValue(thisObject->d->callee.get()); + return true; + } + thisObject->createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + thisObject->createStrictModeCallerIfNecessary(exec); + + return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +bool Arguments::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + Arguments* thisObject = jsCast<Arguments*>(object); + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + descriptor.setDescriptor(thisObject->argument(i).get(), None); + return true; + } + + if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->d->overrodeLength)) { + descriptor.setDescriptor(jsNumber(thisObject->d->numArguments), DontEnum); + return true; + } + + if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->d->overrodeCallee)) { + if (!thisObject->d->isStrictMode) { + descriptor.setDescriptor(thisObject->d->callee.get(), DontEnum); + return true; + } + thisObject->createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + thisObject->createStrictModeCallerIfNecessary(exec); + + return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); +} + +void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + Arguments* thisObject = jsCast<Arguments*>(object); + for (unsigned i = 0; i < thisObject->d->numArguments; ++i) { + if (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) + propertyNames.add(Identifier(exec, UString::number(i))); + } + if (mode == IncludeDontEnumProperties) { + propertyNames.add(exec->propertyNames().callee); + propertyNames.add(exec->propertyNames().length); + } + JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); +} + +void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + if (i < static_cast<unsigned>(thisObject->d->numArguments) && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + thisObject->argument(i).set(exec->globalData(), thisObject, value); + return; + } + + PutPropertySlot slot; + JSObject::put(thisObject, exec, Identifier(exec, UString::number(i)), value, slot); +} + +void Arguments::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < thisObject->d->numArguments && (!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i])) { + thisObject->argument(i).set(exec->globalData(), thisObject, value); + return; + } + + if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { + thisObject->d->overrodeLength = true; + thisObject->putDirect(exec->globalData(), propertyName, value, DontEnum); + return; + } + + if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { + if (!thisObject->d->isStrictMode) { + thisObject->d->overrodeCallee = true; + thisObject->putDirect(exec->globalData(), propertyName, value, DontEnum); + return; + } + thisObject->createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode) + thisObject->createStrictModeCallerIfNecessary(exec); + + JSObject::put(thisObject, exec, propertyName, value, slot); +} + +bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + if (i < thisObject->d->numArguments) { + if (!thisObject->d->deletedArguments) { + thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); + memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); + } + if (!thisObject->d->deletedArguments[i]) { + thisObject->d->deletedArguments[i] = true; + return true; + } + } + + return JSObject::deleteProperty(thisObject, exec, Identifier(exec, UString::number(i))); +} + +bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + Arguments* thisObject = jsCast<Arguments*>(cell); + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex && i < thisObject->d->numArguments) { + if (!thisObject->d->deletedArguments) { + thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]); + memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments); + } + if (!thisObject->d->deletedArguments[i]) { + thisObject->d->deletedArguments[i] = true; + return true; + } + } + + if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) { + thisObject->d->overrodeLength = true; + return true; + } + + if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) { + if (!thisObject->d->isStrictMode) { + thisObject->d->overrodeCallee = true; + return true; + } + thisObject->createStrictModeCalleeIfNecessary(exec); + } + + if (propertyName == exec->propertyNames().caller && !thisObject->d->isStrictMode) + thisObject->createStrictModeCallerIfNecessary(exec); + + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +void Arguments::tearOff(CallFrame* callFrame) +{ + if (isTornOff()) + return; + + if (!d->numArguments) + return; + + d->registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[d->numArguments]); + d->registers = d->registerArray.get() + CallFrame::offsetFor(d->numArguments + 1); + + if (!callFrame->isInlineCallFrame()) { + for (size_t i = 0; i < d->numArguments; ++i) + argument(i).set(callFrame->globalData(), this, callFrame->argument(i)); + return; + } + + InlineCallFrame* inlineCallFrame = callFrame->inlineCallFrame(); + for (size_t i = 0; i < d->numArguments; ++i) { + ValueRecovery& recovery = inlineCallFrame->arguments[i + 1]; + // In the future we'll support displaced recoveries (indicating that the + // argument was flushed to a different location), but for now we don't do + // that so this code will fail if that were to happen. On the other hand, + // it's much less likely that we'll support in-register recoveries since + // this code does not (easily) have access to registers. + JSValue value; + Register* location = &callFrame->registers()[CallFrame::argumentOffset(i)]; + switch (recovery.technique()) { + case AlreadyInRegisterFile: + value = location->jsValue(); + break; + case AlreadyInRegisterFileAsUnboxedInt32: + value = jsNumber(location->unboxedInt32()); + break; + case AlreadyInRegisterFileAsUnboxedCell: + value = location->unboxedCell(); + break; + case AlreadyInRegisterFileAsUnboxedBoolean: + value = jsBoolean(location->unboxedBoolean()); + break; + case AlreadyInRegisterFileAsUnboxedDouble: +#if USE(JSVALUE64) + value = jsNumber(*bitwise_cast<double*>(location)); +#else + value = location->jsValue(); +#endif + break; + case Constant: + value = recovery.constant(); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + argument(i).set(callFrame->globalData(), this, value); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h new file mode 100644 index 000000000..3564fe447 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -0,0 +1,177 @@ +/* + * 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 "CodeOrigin.h" +#include "JSActivation.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "Interpreter.h" +#include "ObjectConstructor.h" + +namespace JSC { + + struct ArgumentsData { + WTF_MAKE_NONCOPYABLE(ArgumentsData); WTF_MAKE_FAST_ALLOCATED; + public: + ArgumentsData() { } + WriteBarrier<JSActivation> activation; + + unsigned numArguments; + + WriteBarrier<Unknown>* registers; + OwnArrayPtr<WriteBarrier<Unknown> > registerArray; + + OwnArrayPtr<bool> deletedArguments; + + WriteBarrier<JSFunction> callee; + bool overrodeLength : 1; + bool overrodeCallee : 1; + bool overrodeCaller : 1; + bool isStrictMode : 1; + }; + + class Arguments : public JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame) + { + Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame); + return arguments; + } + + enum { MaxArguments = 0x10000 }; + + private: + enum NoParametersType { NoParameters }; + + Arguments(CallFrame*); + Arguments(CallFrame*, NoParametersType); + + public: + static const ClassInfo s_info; + + static void visitChildren(JSCell*, SlotVisitor&); + + void fillArgList(ExecState*, MarkedArgumentBuffer&); + + uint32_t length(ExecState* exec) const + { + if (UNLIKELY(d->overrodeLength)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return d->numArguments; + } + + void copyToArguments(ExecState*, CallFrame*, uint32_t length); + void tearOff(CallFrame*); + bool isTornOff() const { return d->registerArray; } + void didTearOffActivation(JSGlobalData& globalData, JSActivation* activation) + { + if (isTornOff()) + return; + d->activation.set(globalData, this, activation); + d->registers = &activation->registerAt(0); + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + void finishCreation(CallFrame*); + + private: + static void destroy(JSCell*); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + void createStrictModeCallerIfNecessary(ExecState*); + void createStrictModeCalleeIfNecessary(ExecState*); + + WriteBarrier<Unknown>& argument(size_t); + + void init(CallFrame*); + + OwnPtr<ArgumentsData> d; + }; + + Arguments* asArguments(JSValue); + + inline Arguments* asArguments(JSValue value) + { + ASSERT(asObject(value)->inherits(&Arguments::s_info)); + return static_cast<Arguments*>(asObject(value)); + } + + inline Arguments::Arguments(CallFrame* callFrame) + : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) + , d(adoptPtr(new ArgumentsData)) + { + } + + inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) + : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) + , d(adoptPtr(new ArgumentsData)) + { + } + + inline WriteBarrier<Unknown>& Arguments::argument(size_t i) + { + return d->registers[CallFrame::argumentOffset(i)]; + } + + inline void Arguments::finishCreation(CallFrame* callFrame) + { + Base::finishCreation(callFrame->globalData()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = asFunction(callFrame->callee()); + d->numArguments = callFrame->argumentCount(); + d->registers = reinterpret_cast<WriteBarrier<Unknown>*>(callFrame->registers()); + d->callee.set(callFrame->globalData(), this, callee); + d->overrodeLength = false; + d->overrodeCallee = false; + d->overrodeCaller = false; + d->isStrictMode = callFrame->codeBlock()->isStrictMode(); + + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (d->isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame); + } + +} // namespace JSC + +#endif // Arguments_h diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp new file mode 100644 index 000000000..ebcc43781 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2011 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" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*); + +} + +#include "ArrayConstructor.lut.h" + +namespace JSC { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ArrayConstructor); + +const ClassInfo ArrayConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::arrayConstructorTable, CREATE_METHOD_TABLE(ArrayConstructor) }; + +/* Source for ArrayConstructor.lut.h +@begin arrayConstructorTable + isArray arrayConstructorIsArray DontEnum|Function 1 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(ArrayConstructor); + +ArrayConstructor::ArrayConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void ArrayConstructor::finishCreation(ExecState* exec, ArrayPrototype* arrayPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, arrayPrototype->classInfo()->className)); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, arrayPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool ArrayConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, ExecState::arrayConstructorTable(exec), jsCast<ArrayConstructor*>(cell), propertyName, slot); +} + +bool ArrayConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<InternalFunction>(exec, ExecState::arrayConstructorTable(exec), jsCast<ArrayConstructor*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions --------------------------- + +static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + + // 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 constructEmptyArray(exec, globalObject, n); + } + + // otherwise the array is constructed with the arguments in it + return constructArray(exec, globalObject, args); +} + +static EncodedJSValue JSC_HOST_CALL constructWithArrayConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); +} + +ConstructType ArrayConstructor::getConstructData(JSCell*, 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)); +} + +CallType ArrayConstructor::getCallData(JSCell*, 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::s_info))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.h b/Source/JavaScriptCore/runtime/ArrayConstructor.h new file mode 100644 index 000000000..c60571fbd --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2011 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: + typedef InternalFunction Base; + + static ArrayConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ArrayPrototype* arrayPrototype) + { + ArrayConstructor* constructor = new (NotNull, allocateCell<ArrayConstructor>(*exec->heap())) ArrayConstructor(globalObject, structure); + constructor->finishCreation(exec, arrayPrototype); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, ArrayPrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; + + private: + ArrayConstructor(JSGlobalObject*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, 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 000000000..d972693dd --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -0,0 +1,1200 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009, 2011 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 "StringRecursionChecker.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::s_info = {"Array", &JSArray::s_info, 0, ExecState::arrayPrototypeTable, CREATE_METHOD_TABLE(ArrayPrototype)}; + +/* Source for ArrayPrototype.lut.h +@begin arrayPrototypeTable 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, Structure* structure) + : JSArray(globalObject->globalData(), structure) +{ +} + +void ArrayPrototype::finishCreation(JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalData()); + ASSERT(inherits(&s_info)); +} + +bool ArrayPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(cell), propertyName, slot); +} + +bool ArrayPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSArray>(exec, ExecState::arrayPrototypeTable(exec), jsCast<ArrayPrototype*>(object), 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->methodTable()->put(obj, 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(thisValue); + if (!isRealArray && !thisValue.inherits(&JSArray::s_info)) + return throwVMTypeError(exec); + JSArray* thisObj = asArray(thisValue); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + StringRecursionChecker checker(exec, thisObj); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + unsigned totalSize = length ? length - 1 : 0; + Vector<RefPtr<StringImpl>, 256> strBuffer(length); + bool allStrings8Bit = true; + + 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(); + allStrings8Bit = allStrings8Bit && str.is8Bit(); + + if (!strBuffer.data()) { + throwOutOfMemoryError(exec); + } + + if (exec->hadException()) + break; + } + if (!totalSize) + return JSValue::encode(jsEmptyString(exec)); + + if (allStrings8Bit) { + Vector<LChar> 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->characters8(), rep->length()); + } + ASSERT(buffer.size() == totalSize); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); + } + + 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::s_info)) + return throwVMTypeError(exec); + JSObject* thisObj = asArray(thisValue); + + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + StringRecursionChecker checker(exec, thisObj); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + JSStringBuilder strBuffer; + 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); + } + } + + return JSValue::encode(strBuffer.build(exec)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + StringRecursionChecker checker(exec, thisObj); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + JSStringBuilder strBuffer; + + UString separator; + if (!exec->argument(0).isUndefined()) + separator = exec->argument(0).toString(exec); + + unsigned k = 0; + if (isJSArray(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)); + } + + 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.toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + size_t i = 0; + size_t argCount = exec->argumentCount(); + while (1) { + if (curArg.inherits(&JSArray::s_info)) { + unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec); + JSObject* curObject = curArg.toObject(exec); + for (unsigned k = 0; k < length; ++k) { + JSValue v = getProperty(exec, curObject, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (v) + arr->methodTable()->putByIndex(arr, exec, n, v); + n++; + } + } else { + arr->methodTable()->putByIndex(arr, 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(thisValue)) + return JSValue::encode(asArray(thisValue)->pop()); + + JSObject* thisObj = thisValue.toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue result; + if (length == 0) { + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, length - 1); + thisObj->methodTable()->deletePropertyByIndex(thisObj, 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(thisValue) && exec->argumentCount() == 1) { + JSArray* array = asArray(thisValue); + array->push(exec, exec->argument(0)); + return JSValue::encode(jsNumber(array->length())); + } + + JSObject* thisObj = thisValue.toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + for (unsigned n = 0; n < exec->argumentCount(); n++) { + // Check for integer overflow; where safe we can do a fast put by index. + if (length + n >= length) + thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->argument(n)); + else { + PutPropertySlot slot; + Identifier propertyName(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toString(exec)); + thisObj->methodTable()->put(thisObj, exec, propertyName, exec->argument(n), slot); + } + } + JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount())); + putProperty(exec, thisObj, exec->propertyNames().length, newLength); + return JSValue::encode(newLength); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned middle = length / 2; + for (unsigned k = 0; k < middle; k++) { + unsigned lk1 = length - k - 1; + JSValue obj2 = getProperty(exec, thisObj, lk1); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + JSValue obj = getProperty(exec, thisObj, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (obj2) + thisObj->methodTable()->putByIndex(thisObj, exec, k, obj2); + else + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k); + + if (obj) + thisObj->methodTable()->putByIndex(thisObj, exec, lk1, obj); + else + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, lk1); + } + return JSValue::encode(thisObj); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue result; + if (length == 0) { + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, 0); + if (isJSArray(thisObj)) + ((JSArray *)thisObj)->shiftCount(exec, 1); + else { + for (unsigned k = 1; k < length; k++) { + JSValue obj = getProperty(exec, thisObj, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (obj) + thisObj->methodTable()->putByIndex(thisObj, exec, k - 1, obj); + else + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1); + } + thisObj->methodTable()->deletePropertyByIndex(thisObj, 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().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // We return a new array + JSArray* resObj = constructEmptyArray(exec); + JSValue result = resObj; + + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); + + unsigned n = 0; + for (unsigned k = begin; k < end; k++, n++) { + JSValue v = getProperty(exec, thisObj, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (v) + resObj->methodTable()->putByIndex(resObj, exec, n, v); + } + resObj->setLength(n); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (!length || exec->hadException()) + return JSValue::encode(thisObj); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + + if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseMode()) { + 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); + } + + // "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); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + unsigned themin = i; + JSValue minObj = iObj; + for (unsigned j = i + 1; j < length; ++j) { + JSValue jObj = thisObj->get(exec, j); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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, jsUndefined(), 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->methodTable()->putByIndex(thisObj, exec, i, minObj); + thisObj->methodTable()->putByIndex(thisObj, exec, themin, iObj); + } + } + return JSValue::encode(thisObj); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) +{ + // 15.4.4.12 + + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!exec->argumentCount()) + return JSValue::encode(constructEmptyArray(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 = JSArray::tryCreateUninitialized(exec->globalData(), exec->lexicalGlobalObject()->arrayStructure(), deleteCount); + if (!resObj) + return JSValue::encode(throwOutOfMemoryError(exec)); + + JSValue result = resObj; + JSGlobalData& globalData = exec->globalData(); + for (unsigned k = 0; k < deleteCount; k++) { + JSValue v = getProperty(exec, thisObj, k + begin); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + resObj->initializeIndex(globalData, k, v); + } + resObj->completeInitialization(deleteCount); + + unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); + if (additionalArgs != deleteCount) { + if (additionalArgs < deleteCount) { + if ((!begin) && (isJSArray(thisObj))) + ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs); + else { + for (unsigned k = begin; k < length - deleteCount; ++k) { + JSValue v = getProperty(exec, thisObj, k + deleteCount); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (v) + thisObj->methodTable()->putByIndex(thisObj, exec, k + additionalArgs, v); + else + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + additionalArgs); + } + for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k - 1); + } + } else { + if ((!begin) && (isJSArray(thisObj))) + ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount); + else { + for (unsigned k = length - deleteCount; k > begin; --k) { + JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (obj) + thisObj->methodTable()->putByIndex(thisObj, exec, k + additionalArgs - 1, obj); + else + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + additionalArgs - 1); + } + } + } + } + for (unsigned k = 0; k < additionalArgs; ++k) + thisObj->methodTable()->putByIndex(thisObj, 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) +{ + // 15.4.4.13 + + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned nrArgs = exec->argumentCount(); + if ((nrArgs) && (length)) { + if (isJSArray(thisObj)) + ((JSArray *)thisObj)->unshiftCount(exec, nrArgs); + else { + for (unsigned k = length; k > 0; --k) { + JSValue v = getProperty(exec, thisObj, k - 1); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (v) + thisObj->methodTable()->putByIndex(thisObj, exec, k + nrArgs - 1, v); + else + thisObj->methodTable()->deletePropertyByIndex(thisObj, exec, k + nrArgs - 1); + } + } + } + for (unsigned k = 0; k < nrArgs; ++k) + thisObj->methodTable()->putByIndex(thisObj, 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().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSValue applyThis = exec->argument(1); + JSArray* resultArray = constructEmptyArray(exec); + + unsigned filterIndex = 0; + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(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->methodTable()->putByIndex(resultArray, 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); + + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + 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->methodTable()->putByIndex(resultArray, exec, filterIndex++, v); + } + return JSValue::encode(resultArray); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSValue applyThis = exec->argument(1); + + JSArray* resultArray = constructEmptyArray(exec, length); + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(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); + + JSArray::putByIndex(resultArray, 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); + + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + MarkedArgumentBuffer eachArguments; + eachArguments.append(v); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); + resultArray->methodTable()->putByIndex(resultArray, 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().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSValue applyThis = exec->argument(1); + + JSValue result = jsBoolean(true); + + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(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); + + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + 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().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSValue applyThis = exec->argument(1); + + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(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); + + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + call(exec, function, callType, callData, applyThis, eachArguments); + } + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + JSValue applyThis = exec->argument(1); + + JSValue result = jsBoolean(false); + + unsigned k = 0; + if (callType == CallTypeJS && isJSArray(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); + + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + 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().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + unsigned i = 0; + JSValue rv; + if (!length && exec->argumentCount() == 1) + return throwVMTypeError(exec); + + JSArray* array = 0; + if (isJSArray(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 (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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(jsUndefined()); + 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 (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (!prop) + continue; + + MarkedArgumentBuffer eachArguments; + eachArguments.append(rv); + eachArguments.append(prop); + eachArguments.append(jsNumber(i)); + eachArguments.append(thisObj); + + rv = call(exec, function, callType, callData, jsUndefined(), eachArguments); + } + return JSValue::encode(rv); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec) +{ + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue function = exec->argument(0); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + + unsigned i = 0; + JSValue rv; + if (!length && exec->argumentCount() == 1) + return throwVMTypeError(exec); + + JSArray* array = 0; + if (isJSArray(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 (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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(jsUndefined()); + 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 (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (!prop) + continue; + + MarkedArgumentBuffer eachArguments; + eachArguments.append(rv); + eachArguments.append(prop); + eachArguments.append(jsNumber(idx)); + eachArguments.append(thisObj); + + rv = call(exec, function, callType, callData, jsUndefined(), eachArguments); + } + return JSValue::encode(rv); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec) +{ + // 15.4.4.14 + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); + JSValue searchElement = exec->argument(0); + for (; index < length; ++index) { + JSValue e = getProperty(exec, thisObj, index); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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) +{ + // 15.4.4.15 + JSObject* thisObj = exec->hostThisValue().toObject(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); + if (!length) + return JSValue::encode(jsNumber(-1)); + + unsigned index = length - 1; + if (exec->argumentCount() >= 2) { + JSValue fromValue = exec->argument(1); + 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 (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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 000000000..f49a9a667 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2011 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 { + private: + ArrayPrototype(JSGlobalObject*, Structure*); + + public: + typedef JSArray Base; + + static ArrayPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure); + prototype->finishCreation(globalObject); + return prototype; + } + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(JSGlobalObject*); + }; + +} // namespace JSC + +#endif // ArrayPrototype_h diff --git a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h new file mode 100644 index 000000000..610b8d16c --- /dev/null +++ b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -0,0 +1,56 @@ +// -*- 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 "JSObject.h" + +namespace JSC { + + class BatchedTransitionOptimizer { + WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer); + public: + BatchedTransitionOptimizer(JSGlobalData& globalData, JSObject* object) + : m_globalData(&globalData) + , m_object(object) + { + } + + ~BatchedTransitionOptimizer() + { + if (m_object->structure()->isDictionary()) + m_object->flattenDictionaryObject(*m_globalData); + } + + private: + JSGlobalData* m_globalData; + JSObject* m_object; + }; + +} // namespace JSC + +#endif // BatchedTransitionOptimizer_h diff --git a/Source/JavaScriptCore/runtime/BigInteger.h b/Source/JavaScriptCore/runtime/BigInteger.h new file mode 100644 index 000000000..833829df6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BigInteger.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 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 BigInteger_h +#define BigInteger_h + +#include <wtf/MathExtras.h> + +namespace JSC { + +// This is used in converting the integer part of a number to a string. +class BigInteger { +public: + BigInteger(double number) + { + ASSERT(isfinite(number) && !signbit(number)); + ASSERT(number == floor(number)); + + bool sign; + int32_t exponent; + uint64_t mantissa; + decomposeDouble(number, sign, exponent, mantissa); + ASSERT(!sign && exponent >= 0); + + int32_t zeroBits = exponent - 52; + + if (zeroBits < 0) { + mantissa >>= -zeroBits; + zeroBits = 0; + } + + while (zeroBits >= 32) { + m_values.append(0); + zeroBits -= 32; + } + + // Left align the 53 bits of the mantissa within 96 bits. + uint32_t values[3]; + values[0] = static_cast<uint32_t>(mantissa); + values[1] = static_cast<uint32_t>(mantissa >> 32); + values[2] = 0; + // Shift based on the remainder of the exponent. + if (zeroBits) { + values[2] = values[1] >> (32 - zeroBits); + values[1] = (values[1] << zeroBits) | (values[0] >> (32 - zeroBits)); + values[0] = (values[0] << zeroBits); + } + m_values.append(values[0]); + m_values.append(values[1]); + m_values.append(values[2]); + + // Canonicalize; remove all trailing zeros. + while (m_values.size() && !m_values.last()) + m_values.removeLast(); + } + + uint32_t divide(uint32_t divisor) + { + uint32_t carry = 0; + + for (size_t i = m_values.size(); i; ) { + --i; + uint64_t dividend = (static_cast<uint64_t>(carry) << 32) + static_cast<uint64_t>(m_values[i]); + + uint64_t result = dividend / static_cast<uint64_t>(divisor); + ASSERT(result == static_cast<uint32_t>(result)); + uint64_t remainder = dividend % static_cast<uint64_t>(divisor); + ASSERT(remainder == static_cast<uint32_t>(remainder)); + + m_values[i] = static_cast<uint32_t>(result); + carry = static_cast<uint32_t>(remainder); + } + + // Canonicalize; remove all trailing zeros. + while (m_values.size() && !m_values.last()) + m_values.removeLast(); + + return carry; + } + + bool operator!() { return !m_values.size(); } + +private: + Vector<uint32_t, 36> m_values; +}; + +} + +#endif + diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.cpp b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp new file mode 100644 index 000000000..a54d281a7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp @@ -0,0 +1,87 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(BooleanConstructor); + +const ClassInfo BooleanConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(BooleanConstructor) }; + +BooleanConstructor::BooleanConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void BooleanConstructor::finishCreation(ExecState* exec, BooleanPrototype* booleanPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, booleanPrototype->classInfo()->className)); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, booleanPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +// ECMA 15.6.2 +JSObject* constructBoolean(ExecState* exec, const ArgList& args) +{ + BooleanObject* obj = BooleanObject::create(exec->globalData(), asInternalFunction(exec->callee())->globalObject()->booleanObjectStructure()); + obj->setInternalValue(exec->globalData(), 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(JSCell*, 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(JSCell*, CallData& callData) +{ + callData.native.function = callBooleanConstructor; + return CallTypeHost; +} + +JSObject* constructBooleanFromImmediateBoolean(ExecState* exec, JSGlobalObject* globalObject, JSValue immediateBooleanValue) +{ + BooleanObject* obj = BooleanObject::create(exec->globalData(), globalObject->booleanObjectStructure()); + obj->setInternalValue(exec->globalData(), immediateBooleanValue); + return obj; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.h b/Source/JavaScriptCore/runtime/BooleanConstructor.h new file mode 100644 index 000000000..2b6bafa2f --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.h @@ -0,0 +1,62 @@ +/* + * 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: + typedef InternalFunction Base; + + static BooleanConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, BooleanPrototype* booleanPrototype) + { + BooleanConstructor* constructor = new (NotNull, allocateCell<BooleanConstructor>(*exec->heap())) BooleanConstructor(globalObject, structure); + constructor->finishCreation(exec, booleanPrototype); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, BooleanPrototype*); + + private: + BooleanConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + }; + + JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSGlobalObject*, 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 000000000..37c6eab0d --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanObject.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 "BooleanObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(BooleanObject); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(BooleanObject); + +const ClassInfo BooleanObject::s_info = { "Boolean", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(BooleanObject) }; + +BooleanObject::BooleanObject(JSGlobalData& globalData, Structure* structure) + : JSWrapperObject(globalData, structure) +{ +} + +void BooleanObject::finishCreation(JSGlobalData& globalData) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanObject.h b/Source/JavaScriptCore/runtime/BooleanObject.h new file mode 100644 index 000000000..2704ff3cd --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanObject.h @@ -0,0 +1,61 @@ +/* + * 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 { + protected: + BooleanObject(JSGlobalData&, Structure*); + void finishCreation(JSGlobalData&); + + public: + typedef JSWrapperObject Base; + + static BooleanObject* create(JSGlobalData& globalData, Structure* structure) + { + BooleanObject* boolean = new (NotNull, allocateCell<BooleanObject>(globalData.heap)) BooleanObject(globalData, structure); + boolean->finishCreation(globalData); + return boolean; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + }; + + BooleanObject* asBooleanObject(JSValue); + + inline BooleanObject* asBooleanObject(JSValue value) + { + ASSERT(asObject(value)->inherits(&BooleanObject::s_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 000000000..b9605d0cf --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008, 2011 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" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState*); + +} + +#include "BooleanPrototype.lut.h" + +namespace JSC { + +const ClassInfo BooleanPrototype::s_info = { "Boolean", &BooleanObject::s_info, 0, ExecState::booleanPrototypeTable, CREATE_METHOD_TABLE(BooleanPrototype) }; + +/* Source for BooleanPrototype.lut.h +@begin booleanPrototypeTable + toString booleanProtoFuncToString DontEnum|Function 0 + valueOf booleanProtoFuncValueOf DontEnum|Function 0 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(BooleanPrototype); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(BooleanPrototype); + +BooleanPrototype::BooleanPrototype(ExecState* exec, Structure* structure) + : BooleanObject(exec->globalData(), structure) +{ +} + +void BooleanPrototype::finishCreation(ExecState* exec, JSGlobalObject*) +{ + Base::finishCreation(exec->globalData()); + setInternalValue(exec->globalData(), jsBoolean(false)); + + ASSERT(inherits(&s_info)); +} + +bool BooleanPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<BooleanObject>(exec, ExecState::booleanPrototypeTable(exec), jsCast<BooleanPrototype*>(cell), propertyName, slot); +} + +bool BooleanPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<BooleanObject>(exec, ExecState::booleanPrototypeTable(exec), jsCast<BooleanPrototype*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions --------------------------- + +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::s_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::s_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 000000000..f35d586e6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 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: + typedef BooleanObject Base; + + static BooleanPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + BooleanPrototype* prototype = new (NotNull, allocateCell<BooleanPrototype>(*exec->heap())) BooleanPrototype(exec, structure); + prototype->finishCreation(exec, globalObject); + return prototype; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, JSGlobalObject*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | BooleanObject::StructureFlags; + + private: + BooleanPrototype(ExecState*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + }; + +} // namespace JSC + +#endif // BooleanPrototype_h diff --git a/Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h b/Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h new file mode 100644 index 000000000..f31b4a07f --- /dev/null +++ b/Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h @@ -0,0 +1,101 @@ +/* + * 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 { + +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 = std::numeric_limits<double>::quiet_NaN(); + m_cache[x].result = std::numeric_limits<double>::quiet_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 000000000..ff71fa0cb --- /dev/null +++ b/Source/JavaScriptCore/runtime/CallData.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 "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); + ASSERT(isValidThisObject(thisValue, exec)); + 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 000000000..b138f5484 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CallData.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 CallData_h +#define CallData_h + +#include "JSValue.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 000000000..9fff64e67 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -0,0 +1,198 @@ +/* + * 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" +#include "ConstructData.h" +#include "JSCell.h" + +namespace JSC { + + class HashEntry; + struct HashTable; + + struct MethodTable { + typedef void (*DestroyFunctionPtr)(JSCell*); + DestroyFunctionPtr destroy; + + typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); + VisitChildrenFunctionPtr visitChildren; + + typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); + GetCallDataFunctionPtr getCallData; + + typedef ConstructType (*GetConstructDataFunctionPtr)(JSCell*, ConstructData&); + GetConstructDataFunctionPtr getConstructData; + + typedef void (*PutFunctionPtr)(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + PutFunctionPtr put; + + typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue); + PutByIndexFunctionPtr putByIndex; + + typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, const Identifier&); + DeletePropertyFunctionPtr deleteProperty; + + typedef bool (*DeletePropertyByIndexFunctionPtr)(JSCell*, ExecState*, unsigned); + DeletePropertyByIndexFunctionPtr deletePropertyByIndex; + + typedef bool (*GetOwnPropertySlotFunctionPtr)(JSCell*, ExecState*, const Identifier&, PropertySlot&); + GetOwnPropertySlotFunctionPtr getOwnPropertySlot; + + typedef bool (*GetOwnPropertySlotByIndexFunctionPtr)(JSCell*, ExecState*, unsigned, PropertySlot&); + GetOwnPropertySlotByIndexFunctionPtr getOwnPropertySlotByIndex; + + typedef JSObject* (*ToThisObjectFunctionPtr)(JSCell*, ExecState*); + ToThisObjectFunctionPtr toThisObject; + + typedef void (*DefineGetterFunctionPtr)(JSObject*, ExecState*, const Identifier&, JSObject*, unsigned); + DefineGetterFunctionPtr defineGetter; + + typedef void (*DefineSetterFunctionPtr)(JSObject*, ExecState*, const Identifier&, JSObject*, unsigned); + DefineSetterFunctionPtr defineSetter; + + typedef JSValue (*DefaultValueFunctionPtr)(const JSObject*, ExecState*, PreferredPrimitiveType); + DefaultValueFunctionPtr defaultValue; + + typedef void (*GetOwnPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; + + typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetPropertyNamesFunctionPtr getPropertyNames; + + typedef UString (*ClassNameFunctionPtr)(const JSObject*); + ClassNameFunctionPtr className; + + typedef bool (*HasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue, JSValue); + HasInstanceFunctionPtr hasInstance; + + typedef void (*PutWithAttributesFunctionPtr)(JSObject*, ExecState*, const Identifier& propertyName, JSValue, unsigned attributes); + PutWithAttributesFunctionPtr putWithAttributes; + + typedef bool (*DefineOwnPropertyFunctionPtr)(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&, bool); + DefineOwnPropertyFunctionPtr defineOwnProperty; + + typedef bool (*GetOwnPropertyDescriptorFunctionPtr)(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + GetOwnPropertyDescriptorFunctionPtr getOwnPropertyDescriptor; + }; + +#define CREATE_MEMBER_CHECKER(member) \ +template <typename T> \ +struct MemberCheck##member { \ + struct Fallback { \ + void member(...); \ + }; \ + struct Derived : T, Fallback { }; \ + template <typename U, U> struct Check; \ + typedef char Yes[2]; \ + typedef char No[1]; \ + template <typename U> \ + static No &func(Check<void (Fallback::*)(...), &U::member>*); \ + template <typename U> \ + static Yes &func(...); \ + enum { has = sizeof(func<Derived>(0)) == sizeof(Yes) }; \ +} + +#define HAS_MEMBER_NAMED(klass, name) (MemberCheck##name<klass>::has) + +#define CREATE_METHOD_TABLE(ClassName) { \ + &ClassName::destroy, \ + &ClassName::visitChildren, \ + &ClassName::getCallData, \ + &ClassName::getConstructData, \ + &ClassName::put, \ + &ClassName::putByIndex, \ + &ClassName::deleteProperty, \ + &ClassName::deletePropertyByIndex, \ + &ClassName::getOwnPropertySlot, \ + &ClassName::getOwnPropertySlotByIndex, \ + &ClassName::toThisObject, \ + &ClassName::defineGetter, \ + &ClassName::defineSetter, \ + &ClassName::defaultValue, \ + &ClassName::getOwnPropertyNames, \ + &ClassName::getPropertyNames, \ + &ClassName::className, \ + &ClassName::hasInstance, \ + &ClassName::putWithAttributes, \ + &ClassName::defineOwnProperty, \ + &ClassName::getOwnPropertyDescriptor, \ + }, \ + sizeof(ClassName), \ + ClassName::TypedArrayStorageType + + 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; + } + + bool isSubClassOf(const ClassInfo* other) const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci == other) + return true; + } + return false; + } + + bool hasStaticProperties() const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci->staticPropHashTable || ci->classPropHashTableGetterFunction) + return true; + } + return false; + } + + const HashTable* staticPropHashTable; + typedef const HashTable* (*ClassPropHashTableGetterFunction)(ExecState*); + const ClassPropHashTableGetterFunction classPropHashTableGetterFunction; + + MethodTable methodTable; + + size_t cellSize; + + TypedArrayType typedArrayStorageType; + }; + +} // namespace JSC + +#endif // ClassInfo_h diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp new file mode 100644 index 000000000..82beda336 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp @@ -0,0 +1,42 @@ +/* + * 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) +#define INITIALIZE_KEYWORD(name) , name##Keyword(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_KEYWORD(INITIALIZE_KEYWORD) + 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 000000000..08d8644b5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -0,0 +1,154 @@ +/* + * 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(bind) \ + macro(call) \ + macro(callee) \ + macro(caller) \ + macro(compile) \ + macro(configurable) \ + macro(constructor) \ + macro(enumerable) \ + macro(eval) \ + macro(exec) \ + macro(fromCharCode) \ + macro(global) \ + macro(get) \ + macro(hasOwnProperty) \ + macro(ignoreCase) \ + macro(index) \ + macro(input) \ + macro(isArray) \ + macro(isPrototypeOf) \ + 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) \ + macro(undefined) + +#define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \ + macro(null) \ + macro(true) \ + macro(false) \ + macro(break) \ + macro(case) \ + macro(catch) \ + macro(const) \ + macro(default) \ + macro(finally) \ + macro(for) \ + macro(instanceof) \ + macro(new) \ + macro(var) \ + macro(continue) \ + macro(function) \ + macro(return) \ + macro(void) \ + macro(delete) \ + macro(if) \ + macro(this) \ + macro(do) \ + macro(while) \ + macro(else) \ + macro(in) \ + macro(switch) \ + macro(throw) \ + macro(try) \ + macro(typeof) \ + macro(with) \ + macro(debugger) \ + macro(class) \ + macro(enum) \ + macro(export) \ + macro(extends) \ + macro(import) \ + macro(super) \ + macro(implements) \ + macro(interface) \ + macro(let) \ + macro(package) \ + macro(private) \ + macro(protected) \ + macro(public) \ + macro(static) \ + macro(yield) + +namespace JSC { + + class CommonIdentifiers { + WTF_MAKE_NONCOPYABLE(CommonIdentifiers); WTF_MAKE_FAST_ALLOCATED; + 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_KEYWORD_NAME_GLOBAL(name) const Identifier name##Keyword; + JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(JSC_IDENTIFIER_DECLARE_KEYWORD_NAME_GLOBAL) +#undef JSC_IDENTIFIER_DECLARE_KEYWORD_NAME_GLOBAL + +#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 000000000..c3d9e6947 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Completion.cpp @@ -0,0 +1,84 @@ +/* + * 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 { + +bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedException) +{ + JSLock lock(exec); + ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable()); + + ProgramExecutable* program = ProgramExecutable::create(exec, source); + JSObject* error = program->checkSyntax(exec); + if (error) { + if (returnedException) + *returnedException = error; + return false; + } + + return true; +} + +JSValue evaluate(ExecState* exec, ScopeChainNode* scopeChain, const SourceCode& source, JSValue thisValue, JSValue* returnedException) +{ + JSLock lock(exec); + ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable()); + + ProgramExecutable* program = ProgramExecutable::create(exec, source); + if (!program) { + if (returnedException) + *returnedException = exec->globalData().exception; + + exec->globalData().exception = JSValue(); + return jsUndefined(); + } + + if (!thisValue || thisValue.isUndefinedOrNull()) + thisValue = exec->dynamicGlobalObject(); + JSObject* thisObj = thisValue.toThisObject(exec); + JSValue result = exec->interpreter()->execute(program, exec, scopeChain, thisObj); + + if (exec->hadException()) { + if (returnedException) + *returnedException = exec->exception(); + + exec->clearException(); + return jsUndefined(); + } + + ASSERT(result); + return result; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Completion.h b/Source/JavaScriptCore/runtime/Completion.h new file mode 100644 index 000000000..c3dbdfc58 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Completion.h @@ -0,0 +1,39 @@ +/* + * 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 ScopeChainNode; + class SourceCode; + + bool checkSyntax(ExecState*, const SourceCode&, JSValue* exception = 0); + JSValue evaluate(ExecState*, ScopeChainNode*, const SourceCode&, JSValue thisValue = JSValue(), JSValue* exception = 0); + +} // namespace JSC + +#endif // Completion_h diff --git a/Source/JavaScriptCore/runtime/ConstructData.cpp b/Source/JavaScriptCore/runtime/ConstructData.cpp new file mode 100644 index 000000000..5da2a911c --- /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 000000000..cc8f46977 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstructData.h @@ -0,0 +1,63 @@ +/* + * 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 "CallData.h" +#include "JSValue.h" + +namespace JSC { + + class ArgList; + class ExecState; + class FunctionExecutable; + class JSObject; + class ScopeChainNode; + + enum ConstructType { + ConstructTypeNone, + ConstructTypeHost, + ConstructTypeJS + }; + + union ConstructData { + struct { + NativeFunction 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 000000000..79c5181eb --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConstructor.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 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 "JSDateMath.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "ObjectPrototype.h" +#include <math.h> +#include <time.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 { + +static EncodedJSValue JSC_HOST_CALL dateParse(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateNow(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateUTC(ExecState*); + +} + +#include "DateConstructor.lut.h" + +namespace JSC { + +const ClassInfo DateConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::dateConstructorTable, CREATE_METHOD_TABLE(DateConstructor) }; + +/* Source for DateConstructor.lut.h +@begin dateConstructorTable + parse dateParse DontEnum|Function 1 + UTC dateUTC DontEnum|Function 7 + now dateNow DontEnum|Function 0 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(DateConstructor); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(DateConstructor); + +DateConstructor::DateConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void DateConstructor::finishCreation(ExecState* exec, DatePrototype* datePrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, datePrototype->classInfo()->className)); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, datePrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(7), ReadOnly | DontEnum | DontDelete); +} + +bool DateConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, ExecState::dateConstructorTable(exec), jsCast<DateConstructor*>(cell), propertyName, slot); +} + +bool DateConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<InternalFunction>(exec, ExecState::dateConstructorTable(exec), jsCast<DateConstructor*>(object), propertyName, descriptor); +} + +// ECMA 15.9.3 +JSObject* constructDate(ExecState* exec, JSGlobalObject* globalObject, 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::s_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 (!isfinite(doubleArguments[0]) + || !isfinite(doubleArguments[1]) + || (numArgs >= 3 && !isfinite(doubleArguments[2])) + || (numArgs >= 4 && !isfinite(doubleArguments[3])) + || (numArgs >= 5 && !isfinite(doubleArguments[4])) + || (numArgs >= 6 && !isfinite(doubleArguments[5])) + || (numArgs >= 7 && !isfinite(doubleArguments[6]))) + value = std::numeric_limits<double>::quiet_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 DateInstance::create(exec, globalObject->dateStructure(), value); +} + +static EncodedJSValue JSC_HOST_CALL constructWithDateConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructDate(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +ConstructType DateConstructor::getConstructData(JSCell*, 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(JSCell*, 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 000000000..fe6597418 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConstructor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 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: + typedef InternalFunction Base; + + static DateConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, DatePrototype* datePrototype) + { + DateConstructor* constructor = new (NotNull, allocateCell<DateConstructor>(*exec->heap())) DateConstructor(globalObject, structure); + constructor->finishCreation(exec, datePrototype); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, DatePrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; + + private: + DateConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + }; + + JSObject* constructDate(ExecState*, JSGlobalObject*, 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 000000000..7bc0cbc0f --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConversion.cpp @@ -0,0 +1,107 @@ +/* + * 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 "JSDateMath.h" +#include "JSObject.h" +#include "ScopeChain.h" +#include "UString.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 000000000..ff32b503d --- /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 000000000..a502770c8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstance.cpp @@ -0,0 +1,93 @@ +/* + * 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 "JSDateMath.h" +#include "JSGlobalObject.h" + +#include <math.h> +#include <wtf/MathExtras.h> + +using namespace WTF; + +namespace JSC { + +const ClassInfo DateInstance::s_info = {"Date", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(DateInstance)}; + +DateInstance::DateInstance(ExecState* exec, Structure* structure) + : JSWrapperObject(exec->globalData(), structure) +{ +} + +void DateInstance::finishCreation(JSGlobalData& globalData) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + setInternalValue(globalData, jsNaN()); +} + +void DateInstance::finishCreation(JSGlobalData& globalData, double time) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + setInternalValue(globalData, jsNumber(timeClip(time))); +} + +void DateInstance::destroy(JSCell* cell) +{ + jsCast<DateInstance*>(cell)->DateInstance::~DateInstance(); +} + +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 000000000..3edfb0970 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstance.h @@ -0,0 +1,97 @@ +/* + * 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 { + protected: + DateInstance(ExecState*, Structure*); + void finishCreation(JSGlobalData&); + void finishCreation(JSGlobalData&, double); + + static void destroy(JSCell*); + + public: + typedef JSWrapperObject Base; + + static DateInstance* create(ExecState* exec, Structure* structure, double date) + { + DateInstance* instance = new (NotNull, allocateCell<DateInstance>(*exec->heap())) DateInstance(exec, structure); + instance->finishCreation(exec->globalData(), date); + return instance; + } + + static DateInstance* create(ExecState* exec, Structure* structure) + { + DateInstance* instance = new (NotNull, allocateCell<DateInstance>(*exec->heap())) DateInstance(exec, structure); + instance->finishCreation(exec->globalData()); + return instance; + } + + double internalNumber() const { return internalValue().asNumber(); } + + static JS_EXPORTDATA const ClassInfo s_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 Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + private: + const GregorianDateTime* calculateGregorianDateTime(ExecState*) const; + const GregorianDateTime* calculateGregorianDateTimeUTC(ExecState*) const; + + mutable RefPtr<DateInstanceData> m_data; + }; + + DateInstance* asDateInstance(JSValue); + + inline DateInstance* asDateInstance(JSValue value) + { + ASSERT(asObject(value)->inherits(&DateInstance::s_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 000000000..153582f67 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstanceCache.h @@ -0,0 +1,93 @@ +/* + * 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 "JSDateMath.h" +#include <wtf/FixedArray.h> +#include <wtf/HashFunctions.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace JSC { + + 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(std::numeric_limits<double>::quiet_NaN()) + , m_gregorianDateTimeUTCCachedForMS(std::numeric_limits<double>::quiet_NaN()) + { + } + }; + + class DateInstanceCache { + public: + DateInstanceCache() + { + reset(); + } + + void reset() + { + for (size_t i = 0; i < cacheSize; ++i) + m_cache[i].key = std::numeric_limits<double>::quiet_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 000000000..96dea01db --- /dev/null +++ b/Source/JavaScriptCore/runtime/DatePrototype.cpp @@ -0,0 +1,1109 @@ +/* + * 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 "JSDateMath.h" +#include "JSGlobalObject.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/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) || PLATFORM(IOS) +#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) || PLATFORM(IOS) + +// FIXME: Since this is superior to the strftime-based version, why limit this to PLATFORM(MAC)? +// Instead we should consider using this whenever USE(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) && !PLATFORM(IOS) + +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)) + // strftime() does not support '#' on WinCE + 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) && !PLATFORM(IOS) + +// 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::s_info = {"Date", &DateInstance::s_info, 0, ExecState::dateTable, CREATE_METHOD_TABLE(DatePrototype)}; + +/* 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, Structure* structure) + : DateInstance(exec, structure) +{ +} + +void DatePrototype::finishCreation(ExecState* exec, JSGlobalObject*) +{ + Base::finishCreation(exec->globalData()); + ASSERT(inherits(&s_info)); + + // The constructor will be added later, after DateConstructor has been built. +} + +bool DatePrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, ExecState::dateTable(exec), jsCast<DatePrototype*>(cell), propertyName, slot); +} + +bool DatePrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::dateTable(exec), jsCast<DatePrototype*>(object), propertyName, descriptor); +} + +// Functions + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::s_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::s_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::s_info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!isfinite(thisDateObj->internalNumber())) + return throwVMError(exec, createRangeError(exec, "Invalid Date")); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); + // Maximum amount of space we need in buffer: 7 (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 = 28. We add one extra character to allow us to force null termination. + char buffer[29]; + // If the year is outside the bounds of 0 and 9999 inclusive we want to use the extended year format (ES 15.9.1.15.1). + int ms = static_cast<int>(fmod(thisDateObj->internalNumber(), msPerSecond)); + if (ms < 0) + ms += msPerSecond; + if (gregorianDateTime->year > 8099 || gregorianDateTime->year < -1900) + snprintf(buffer, sizeof(buffer) - 1, "%+07d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + gregorianDateTime->year, gregorianDateTime->month + 1, gregorianDateTime->monthDay, gregorianDateTime->hour, gregorianDateTime->minute, gregorianDateTime->second, ms); + else + 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, ms); + 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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_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::s_info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + double milli = timeClip(exec->argument(0).toNumber(exec)); + JSValue result = jsNumber(milli); + thisDateObj->setInternalValue(exec->globalData(), result); + return JSValue::encode(result); +} + +static EncodedJSValue setNewValueFromTimeArgs(ExecState* exec, int numArgsToUse, bool inputIsUTC) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::s_info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + + if (!exec->argumentCount() || isnan(milli)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(exec->globalData(), 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(exec->globalData(), result); + return JSValue::encode(result); + } + + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); + thisDateObj->setInternalValue(exec->globalData(), result); + return JSValue::encode(result); +} + +static EncodedJSValue setNewValueFromDateArgs(ExecState* exec, int numArgsToUse, bool inputIsUTC) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::s_info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(exec->globalData(), 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(exec->globalData(), result); + return JSValue::encode(result); + } + + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); + thisDateObj->setInternalValue(exec->globalData(), 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::s_info)) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(exec->globalData(), 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(exec->globalData(), 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(exec->globalData(), result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&DateInstance::s_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 000000000..90eec28a1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DatePrototype.h @@ -0,0 +1,61 @@ +/* + * 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 { + private: + DatePrototype(ExecState*, Structure*); + + public: + typedef DateInstance Base; + + static DatePrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + DatePrototype* prototype = new (NotNull, allocateCell<DatePrototype>(*exec->heap())) DatePrototype(exec, structure); + prototype->finishCreation(exec, globalObject); + return prototype; + } + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, JSGlobalObject*); + 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 000000000..24f5da4f2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -0,0 +1,178 @@ +/* + * 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 "FunctionPrototype.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")); +} + +ASSERT_CLASS_FITS_IN_CELL(StrictModeTypeErrorFunction); + +const ClassInfo StrictModeTypeErrorFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(StrictModeTypeErrorFunction) }; + +void StrictModeTypeErrorFunction::destroy(JSCell* cell) +{ + jsCast<StrictModeTypeErrorFunction*>(cell)->StrictModeTypeErrorFunction::~StrictModeTypeErrorFunction(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h new file mode 100644 index 000000000..b807d4ab4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Error.h @@ -0,0 +1,132 @@ +/* + * 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 "InternalFunction.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)); } + + class StrictModeTypeErrorFunction : public InternalFunction { + private: + StrictModeTypeErrorFunction(JSGlobalObject* globalObject, Structure* structure, const UString& message) + : InternalFunction(globalObject, structure) + , m_message(message) + { + } + + static void destroy(JSCell*); + + public: + typedef InternalFunction Base; + + static StrictModeTypeErrorFunction* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, const UString& message) + { + StrictModeTypeErrorFunction* function = new (NotNull, allocateCell<StrictModeTypeErrorFunction>(*exec->heap())) StrictModeTypeErrorFunction(globalObject, structure, message); + function->finishCreation(exec->globalData(), exec->globalData().propertyNames->emptyIdentifier); + return function; + } + + static EncodedJSValue JSC_HOST_CALL constructThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + static ConstructType getConstructData(JSCell*, 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()); + } + + static CallType getCallData(JSCell*, CallData& callData) + { + callData.native.function = callThrowTypeError; + return CallTypeHost; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + private: + UString m_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 000000000..c8f93ba32 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp @@ -0,0 +1,76 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ErrorConstructor); + +const ClassInfo ErrorConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ErrorConstructor) }; + +ErrorConstructor::ErrorConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void ErrorConstructor::finishCreation(ExecState* exec, ErrorPrototype* errorPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, errorPrototype->classInfo()->className)); + // ECMA 15.11.3.1 Error.prototype + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, errorPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(exec->globalData(), 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 = asInternalFunction(exec->callee())->globalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +ConstructType ErrorConstructor::getConstructData(JSCell*, 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 = asInternalFunction(exec->callee())->globalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +CallType ErrorConstructor::getCallData(JSCell*, 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 000000000..58399a9bc --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.h @@ -0,0 +1,60 @@ +/* + * 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: + typedef InternalFunction Base; + + static ErrorConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ErrorPrototype* errorPrototype) + { + ErrorConstructor* constructor = new (NotNull, allocateCell<ErrorConstructor>(*exec->heap())) ErrorConstructor(globalObject, structure); + constructor->finishCreation(exec, errorPrototype); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, ErrorPrototype*); + + private: + ErrorConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, 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 000000000..91a6fc40e --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorInstance.cpp @@ -0,0 +1,36 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ErrorInstance); + +const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(ErrorInstance) }; + +ErrorInstance::ErrorInstance(JSGlobalData& globalData, Structure* structure) + : JSNonFinalObject(globalData, structure) + , m_appendSourceToMessage(false) +{ +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.h b/Source/JavaScriptCore/runtime/ErrorInstance.h new file mode 100644 index 000000000..888bfe856 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorInstance.h @@ -0,0 +1,75 @@ +/* + * 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 JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ErrorInstanceType, StructureFlags), &s_info); + } + + static ErrorInstance* create(JSGlobalData& globalData, Structure* structure, const UString& message) + { + ErrorInstance* instance = new (NotNull, allocateCell<ErrorInstance>(globalData.heap)) ErrorInstance(globalData, structure); + instance->finishCreation(globalData, message); + return instance; + } + static ErrorInstance* create(ExecState* exec, Structure* structure, JSValue message) + { + if (message.isUndefined()) { + ErrorInstance* instance = new (NotNull, allocateCell<ErrorInstance>(*exec->heap())) ErrorInstance(exec->globalData(), structure); + instance->finishCreation(exec->globalData(), UString()); + return instance; + } + return create(exec->globalData(), structure, message.toString(exec)); + } + + bool appendSourceToMessage() { return m_appendSourceToMessage; } + void setAppendSourceToMessage() { m_appendSourceToMessage = true; } + void clearAppendSourceToMessage() { m_appendSourceToMessage = false; } + + protected: + explicit ErrorInstance(JSGlobalData&, Structure*); + + void finishCreation(JSGlobalData& globalData, const UString& message) + { + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + if (!message.isNull()) + putDirect(globalData, globalData.propertyNames->message, jsString(&globalData, message), DontEnum); + } + + 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 000000000..e1a395c0e --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp @@ -0,0 +1,138 @@ +/* + * 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 "Error.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "ObjectPrototype.h" +#include "StringRecursionChecker.h" +#include "UString.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); + +static EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*); + +} + +#include "ErrorPrototype.lut.h" + +namespace JSC { + +const ClassInfo ErrorPrototype::s_info = { "Error", &ErrorInstance::s_info, 0, ExecState::errorPrototypeTable, CREATE_METHOD_TABLE(ErrorPrototype) }; + +/* Source for ErrorPrototype.lut.h +@begin errorPrototypeTable + toString errorProtoFuncToString DontEnum|Function 0 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); + +ErrorPrototype::ErrorPrototype(ExecState* exec, Structure* structure) + : ErrorInstance(exec->globalData(), structure) +{ +} + +void ErrorPrototype::finishCreation(ExecState* exec, JSGlobalObject*) +{ + Base::finishCreation(exec->globalData(), ""); + ASSERT(inherits(&s_info)); + putDirect(exec->globalData(), exec->propertyNames().name, jsNontrivialString(exec, "Error"), DontEnum); +} + +bool ErrorPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<ErrorInstance>(exec, ExecState::errorPrototypeTable(exec), jsCast<ErrorPrototype*>(cell), propertyName, slot); +} + +bool ErrorPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<ErrorInstance>(exec, ExecState::errorPrototypeTable(exec), jsCast<ErrorPrototype*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions --------------------------- + +// ECMA-262 5.1, 15.11.4.4 +EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec) +{ + // 1. Let O be the this value. + JSValue thisValue = exec->hostThisValue(); + + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisValue.isObject()) + return throwVMTypeError(exec); + JSObject* thisObj = asObject(thisValue); + + // Guard against recursion! + StringRecursionChecker checker(exec, thisObj); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + // 3. Let name be the result of calling the [[Get]] internal method of O with argument "name". + JSValue name = thisObj->get(exec, exec->propertyNames().name); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 4. If name is undefined, then let name be "Error"; else let name be ToString(name). + UString nameString; + if (name.isUndefined()) + nameString = "Error"; + else { + nameString = name.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + // 5. Let msg be the result of calling the [[Get]] internal method of O with argument "message". + JSValue message = thisObj->get(exec, exec->propertyNames().message); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // (sic) + // 6. If msg is undefined, then let msg be the empty String; else let msg be ToString(msg). + // 7. If msg is undefined, then let msg be the empty String; else let msg be ToString(msg). + UString messageString; + if (message.isUndefined()) + messageString = ""; + else { + messageString = message.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + // 8. If name is the empty String, return msg. + if (!nameString.length()) + return JSValue::encode(message.isString() ? message : jsString(exec, messageString)); + + // 9. If msg is the empty String, return name. + if (!messageString.length()) + return JSValue::encode(name.isString() ? name : jsNontrivialString(exec, nameString)); + + // 10. Return the result of concatenating name, ":", a single space character, and msg. + return JSValue::encode(jsMakeNontrivialString(exec, nameString, ": ", messageString)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorPrototype.h b/Source/JavaScriptCore/runtime/ErrorPrototype.h new file mode 100644 index 000000000..6f02583a6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.h @@ -0,0 +1,61 @@ +/* + * 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: + typedef ErrorInstance Base; + + static ErrorPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + ErrorPrototype* prototype = new (NotNull, allocateCell<ErrorPrototype>(*exec->heap())) ErrorPrototype(exec, structure); + prototype->finishCreation(exec, globalObject); + return prototype; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ErrorInstanceType, StructureFlags), &s_info); + } + + protected: + ErrorPrototype(ExecState*, Structure*); + void finishCreation(ExecState*, JSGlobalObject*); + + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ErrorInstance::StructureFlags; + + private: + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + }; + +} // namespace JSC + +#endif // ErrorPrototype_h diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp new file mode 100644 index 000000000..05e971f7f --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -0,0 +1,170 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(InterruptedExecutionError); + +const ClassInfo InterruptedExecutionError::s_info = { "InterruptedExecutionError", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(InterruptedExecutionError) }; + +JSValue InterruptedExecutionError::defaultValue(const JSObject*, ExecState* exec, PreferredPrimitiveType hint) +{ + if (hint == PreferString) + return jsNontrivialString(exec, "JavaScript execution exceeded timeout."); + return JSValue(std::numeric_limits<double>::quiet_NaN()); +} + +JSObject* createInterruptedExecutionException(JSGlobalData* globalData) +{ + return InterruptedExecutionError::create(*globalData); +} + +bool isInterruptedExecutionException(JSObject* object) +{ + return object->inherits(&InterruptedExecutionError::s_info); +} + +bool isInterruptedExecutionException(JSValue value) +{ + return value.inherits(&InterruptedExecutionError::s_info); +} + + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(TerminatedExecutionError); + +const ClassInfo TerminatedExecutionError::s_info = { "TerminatedExecutionError", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(TerminatedExecutionError) }; + +JSValue TerminatedExecutionError::defaultValue(const JSObject*, ExecState* exec, PreferredPrimitiveType hint) +{ + if (hint == PreferString) + return jsNontrivialString(exec, "JavaScript execution terminated."); + return JSValue(std::numeric_limits<double>::quiet_NaN()); +} + +JSObject* createTerminatedExecutionException(JSGlobalData* globalData) +{ + return TerminatedExecutionError::create(*globalData); +} + +bool isTerminatedExecutionException(JSObject* object) +{ + return object->inherits(&TerminatedExecutionError::s_info); +} + +bool isTerminatedExecutionException(JSValue value) +{ + return value.inherits(&TerminatedExecutionError::s_info); +} + + +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* createOutOfMemoryError(JSGlobalObject* globalObject) +{ + return createError(globalObject, "Out of memory"); +} + +JSObject* throwOutOfMemoryError(ExecState* exec) +{ + return throwError(exec, createOutOfMemoryError(exec->lexicalGlobalObject())); +} + +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 000000000..f71d1a308 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h @@ -0,0 +1,114 @@ +/* + * 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 "JSObject.h" + +namespace JSC { + +JSObject* createInterruptedExecutionException(JSGlobalData*); +bool isInterruptedExecutionException(JSObject*); +bool isInterruptedExecutionException(JSValue); + +JSObject* createTerminatedExecutionException(JSGlobalData*); +bool isTerminatedExecutionException(JSObject*); +bool isTerminatedExecutionException(JSValue); + +JSObject* createStackOverflowError(ExecState*); +JSObject* createStackOverflowError(JSGlobalObject*); +JSObject* createOutOfMemoryError(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*); + + +class InterruptedExecutionError : public JSNonFinalObject { +private: + InterruptedExecutionError(JSGlobalData& globalData) + : JSNonFinalObject(globalData, globalData.interruptedExecutionErrorStructure.get()) + { + } + + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + +public: + typedef JSNonFinalObject Base; + + static InterruptedExecutionError* create(JSGlobalData& globalData) + { + InterruptedExecutionError* error = new (NotNull, allocateCell<InterruptedExecutionError>(globalData.heap)) InterruptedExecutionError(globalData); + error->finishCreation(globalData); + return error; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; +}; + +class TerminatedExecutionError : public JSNonFinalObject { +private: + TerminatedExecutionError(JSGlobalData& globalData) + : JSNonFinalObject(globalData, globalData.terminatedExecutionErrorStructure.get()) + { + } + + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + +public: + typedef JSNonFinalObject Base; + + static TerminatedExecutionError* create(JSGlobalData& globalData) + { + TerminatedExecutionError* error = new (NotNull, allocateCell<TerminatedExecutionError>(globalData.heap)) TerminatedExecutionError(globalData); + error->finishCreation(globalData); + return error; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; +}; + +} // namespace JSC + +#endif // ExceptionHelpers_h diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp new file mode 100644 index 000000000..ad86463db --- /dev/null +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -0,0 +1,756 @@ +/* + * 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 "DFGDriver.h" +#include "JIT.h" +#include "Parser.h" +#include "UStringBuilder.h" +#include "Vector.h" + +namespace JSC { + +const ClassInfo ExecutableBase::s_info = { "Executable", 0, 0, 0, CREATE_METHOD_TABLE(ExecutableBase) }; + +void ExecutableBase::destroy(JSCell* cell) +{ + jsCast<ExecutableBase*>(cell)->ExecutableBase::~ExecutableBase(); +} + +inline void ExecutableBase::clearCode() +{ +#if ENABLE(JIT) + m_jitCodeForCall.clear(); + m_jitCodeForConstruct.clear(); + m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr(); + m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr(); +#endif + m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED; + m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED; +} + +#if ENABLE(DFG_JIT) +Intrinsic ExecutableBase::intrinsic() const +{ + if (const NativeExecutable* nativeExecutable = jsDynamicCast<const NativeExecutable*>(this)) + return nativeExecutable->intrinsic(); + return NoIntrinsic; +} +#endif + +const ClassInfo NativeExecutable::s_info = { "NativeExecutable", &ExecutableBase::s_info, 0, 0, CREATE_METHOD_TABLE(NativeExecutable) }; + +void NativeExecutable::destroy(JSCell* cell) +{ + jsCast<NativeExecutable*>(cell)->NativeExecutable::~NativeExecutable(); +} + +#if ENABLE(DFG_JIT) +Intrinsic NativeExecutable::intrinsic() const +{ + return m_intrinsic; +} +#endif + +#if ENABLE(JIT) +// Utility method used for jettisoning code blocks. +template<typename T> +static void jettisonCodeBlock(JSGlobalData& globalData, OwnPtr<T>& codeBlock) +{ + ASSERT(codeBlock->getJITType() != JITCode::BaselineJIT); + ASSERT(codeBlock->alternative()); + OwnPtr<T> codeBlockToJettison = codeBlock.release(); + codeBlock = static_pointer_cast<T>(codeBlockToJettison->releaseAlternative()); + codeBlockToJettison->unlinkIncomingCalls(); + globalData.heap.jettisonDFGCodeBlock(static_pointer_cast<CodeBlock>(codeBlockToJettison.release())); +} +#endif + +void NativeExecutable::finalize(JSCell* cell) +{ + jsCast<NativeExecutable*>(cell)->clearCode(); +} + +const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, 0, 0, CREATE_METHOD_TABLE(ScriptExecutable) }; + +void ScriptExecutable::destroy(JSCell* cell) +{ + jsCast<ScriptExecutable*>(cell)->ScriptExecutable::~ScriptExecutable(); +} + +const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &ScriptExecutable::s_info, 0, 0, CREATE_METHOD_TABLE(EvalExecutable) }; + +EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext) + : ScriptExecutable(exec->globalData().evalExecutableStructure.get(), exec, source, inStrictContext) +{ +} + +void EvalExecutable::destroy(JSCell* cell) +{ + jsCast<EvalExecutable*>(cell)->EvalExecutable::~EvalExecutable(); +} + +const ClassInfo ProgramExecutable::s_info = { "ProgramExecutable", &ScriptExecutable::s_info, 0, 0, CREATE_METHOD_TABLE(ProgramExecutable) }; + +ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source) + : ScriptExecutable(exec->globalData().programExecutableStructure.get(), exec, source, false) +{ +} + +void ProgramExecutable::destroy(JSCell* cell) +{ + jsCast<ProgramExecutable*>(cell)->ProgramExecutable::~ProgramExecutable(); +} + +const ClassInfo FunctionExecutable::s_info = { "FunctionExecutable", &ScriptExecutable::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionExecutable) }; + +FunctionExecutable::FunctionExecutable(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext) + : ScriptExecutable(globalData.functionExecutableStructure.get(), globalData, source, inStrictContext) + , m_numCapturedVariables(0) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_name(name) + , m_symbolTable(0) +{ +} + +FunctionExecutable::FunctionExecutable(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext) + : ScriptExecutable(exec->globalData().functionExecutableStructure.get(), exec, source, inStrictContext) + , m_numCapturedVariables(0) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_name(name) + , m_symbolTable(0) +{ +} + +void FunctionExecutable::destroy(JSCell* cell) +{ + jsCast<FunctionExecutable*>(cell)->FunctionExecutable::~FunctionExecutable(); +} + +JSObject* EvalExecutable::compileOptimized(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + ASSERT(exec->globalData().dynamicGlobalObject); + ASSERT(!!m_evalCodeBlock); + JSObject* error = 0; + if (m_evalCodeBlock->getJITType() != JITCode::topTierJIT()) + error = compileInternal(exec, scopeChainNode, JITCode::nextTierJIT(m_evalCodeBlock->getJITType())); + ASSERT(!!m_evalCodeBlock); + return error; +} + +JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode, JITCode::JITType jitType) +{ + SamplingRegion samplingRegion(jitType == JITCode::BaselineJIT ? "Baseline Compilation (TOTAL)" : "DFG Compilation (TOTAL)"); + +#if !ENABLE(JIT) + UNUSED_PARAM(jitType); +#endif + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + + if (!!m_evalCodeBlock && m_evalCodeBlock->canProduceCopyWithBytecode()) { + BytecodeDestructionBlocker blocker(m_evalCodeBlock.get()); + OwnPtr<EvalCodeBlock> newCodeBlock = adoptPtr(new EvalCodeBlock(CodeBlock::CopyParsedBlock, *m_evalCodeBlock)); + newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_evalCodeBlock.release())); + m_evalCodeBlock = newCodeBlock.release(); + } else { + if (!lexicalGlobalObject->evalEnabled()) + return throwError(exec, createEvalError(exec, "Eval is disabled")); + RefPtr<EvalNode> evalNode = parse<EvalNode>(globalData, lexicalGlobalObject, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, EvalNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception); + if (!evalNode) { + ASSERT(exception); + return exception; + } + recordParse(evalNode->features(), evalNode->hasCapturedVariables(), evalNode->lineNo(), evalNode->lastLine()); + + JSGlobalObject* globalObject = scopeChainNode->globalObject.get(); + + OwnPtr<CodeBlock> previousCodeBlock = m_evalCodeBlock.release(); + ASSERT((jitType == JITCode::bottomTierJIT()) == !previousCodeBlock); + m_evalCodeBlock = adoptPtr(new EvalCodeBlock(this, globalObject, source().provider(), scopeChainNode->localDepth(), previousCodeBlock.release())); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(evalNode.get(), scopeChainNode, m_evalCodeBlock->symbolTable(), m_evalCodeBlock.get(), !!m_evalCodeBlock->alternative() ? OptimizingCompilation : FirstCompilation))); + if ((exception = generator->generate())) { + m_evalCodeBlock = static_pointer_cast<EvalCodeBlock>(m_evalCodeBlock->releaseAlternative()); + evalNode->destroyData(); + return exception; + } + + evalNode->destroyData(); + m_evalCodeBlock->copyPostParseDataFromAlternative(); + } + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + bool dfgCompiled = false; + if (jitType == JITCode::DFGJIT) + dfgCompiled = DFG::tryCompile(exec, m_evalCodeBlock.get(), m_jitCodeForCall); + if (dfgCompiled) + ASSERT(!m_evalCodeBlock->alternative() || !m_evalCodeBlock->alternative()->hasIncomingCalls()); + else { + if (m_evalCodeBlock->alternative()) { + // There is already an alternative piece of code compiled with a different + // JIT, so we can silently fail. + m_evalCodeBlock = static_pointer_cast<EvalCodeBlock>(m_evalCodeBlock->releaseAlternative()); + return 0; + } + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_evalCodeBlock.get()); + } +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_evalCodeBlock->handleBytecodeDiscardingOpportunity(); +#endif + m_evalCodeBlock->setJITCode(m_jitCodeForCall, MacroAssemblerCodePtr()); + } +#endif + +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (!m_jitCodeForCall) + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_evalCodeBlock)); + else +#endif + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_evalCodeBlock) + m_jitCodeForCall.size()); +#else + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_evalCodeBlock)); +#endif + + return 0; +} + +#if ENABLE(JIT) +void EvalExecutable::jettisonOptimizedCode(JSGlobalData& globalData) +{ + jettisonCodeBlock(globalData, m_evalCodeBlock); + m_jitCodeForCall = m_evalCodeBlock->getJITCode(); + ASSERT(!m_jitCodeForCallWithArityCheck); +} +#endif + +void EvalExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + EvalExecutable* thisObject = jsCast<EvalExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + ScriptExecutable::visitChildren(thisObject, visitor); + if (thisObject->m_evalCodeBlock) + thisObject->m_evalCodeBlock->visitAggregate(visitor); +} + +void EvalExecutable::unlinkCalls() +{ +#if ENABLE(JIT) + if (!m_jitCodeForCall) + return; + ASSERT(m_evalCodeBlock); + m_evalCodeBlock->unlinkCalls(); +#endif +} + +void EvalExecutable::finalize(JSCell* cell) +{ + jsCast<EvalExecutable*>(cell)->clearCode(); +} + +inline void EvalExecutable::clearCode() +{ + if (m_evalCodeBlock) { + m_evalCodeBlock->clearEvalCache(); + m_evalCodeBlock.clear(); + } + Base::clearCode(); +} + +JSObject* ProgramExecutable::checkSyntax(ExecState* exec) +{ + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> programNode = parse<ProgramNode>(globalData, lexicalGlobalObject, m_source, 0, JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception); + if (programNode) + return 0; + ASSERT(exception); + return exception; +} + +JSObject* ProgramExecutable::compileOptimized(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + ASSERT(exec->globalData().dynamicGlobalObject); + ASSERT(!!m_programCodeBlock); + JSObject* error = 0; + if (m_programCodeBlock->getJITType() != JITCode::topTierJIT()) + error = compileInternal(exec, scopeChainNode, JITCode::nextTierJIT(m_programCodeBlock->getJITType())); + ASSERT(!!m_programCodeBlock); + return error; +} + +JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode, JITCode::JITType jitType) +{ + SamplingRegion samplingRegion(jitType == JITCode::BaselineJIT ? "Baseline Compilation (TOTAL)" : "DFG Compilation (TOTAL)"); + +#if !ENABLE(JIT) + UNUSED_PARAM(jitType); +#endif + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + + if (!!m_programCodeBlock && m_programCodeBlock->canProduceCopyWithBytecode()) { + BytecodeDestructionBlocker blocker(m_programCodeBlock.get()); + OwnPtr<ProgramCodeBlock> newCodeBlock = adoptPtr(new ProgramCodeBlock(CodeBlock::CopyParsedBlock, *m_programCodeBlock)); + newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_programCodeBlock.release())); + m_programCodeBlock = newCodeBlock.release(); + } else { + RefPtr<ProgramNode> programNode = parse<ProgramNode>(globalData, lexicalGlobalObject, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception); + if (!programNode) { + ASSERT(exception); + return exception; + } + recordParse(programNode->features(), programNode->hasCapturedVariables(), programNode->lineNo(), programNode->lastLine()); + + JSGlobalObject* globalObject = scopeChainNode->globalObject.get(); + + OwnPtr<CodeBlock> previousCodeBlock = m_programCodeBlock.release(); + ASSERT((jitType == JITCode::bottomTierJIT()) == !previousCodeBlock); + m_programCodeBlock = adoptPtr(new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider(), previousCodeBlock.release())); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(programNode.get(), scopeChainNode, &globalObject->symbolTable(), m_programCodeBlock.get(), !!m_programCodeBlock->alternative() ? OptimizingCompilation : FirstCompilation))); + if ((exception = generator->generate())) { + m_programCodeBlock = static_pointer_cast<ProgramCodeBlock>(m_programCodeBlock->releaseAlternative()); + programNode->destroyData(); + return exception; + } + + programNode->destroyData(); + m_programCodeBlock->copyPostParseDataFromAlternative(); + } + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + bool dfgCompiled = false; + if (jitType == JITCode::DFGJIT) + dfgCompiled = DFG::tryCompile(exec, m_programCodeBlock.get(), m_jitCodeForCall); + if (dfgCompiled) { + if (m_programCodeBlock->alternative()) + m_programCodeBlock->alternative()->unlinkIncomingCalls(); + } else { + if (m_programCodeBlock->alternative()) { + m_programCodeBlock = static_pointer_cast<ProgramCodeBlock>(m_programCodeBlock->releaseAlternative()); + return 0; + } + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_programCodeBlock.get()); + } +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_programCodeBlock->handleBytecodeDiscardingOpportunity(); +#endif + m_programCodeBlock->setJITCode(m_jitCodeForCall, MacroAssemblerCodePtr()); + } +#endif + +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (!m_jitCodeForCall) + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_programCodeBlock)); + else +#endif + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_programCodeBlock) + m_jitCodeForCall.size()); +#else + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_programCodeBlock)); +#endif + + return 0; +} + +#if ENABLE(JIT) +void ProgramExecutable::jettisonOptimizedCode(JSGlobalData& globalData) +{ + jettisonCodeBlock(globalData, m_programCodeBlock); + m_jitCodeForCall = m_programCodeBlock->getJITCode(); + ASSERT(!m_jitCodeForCallWithArityCheck); +} +#endif + +void ProgramExecutable::unlinkCalls() +{ +#if ENABLE(JIT) + if (!m_jitCodeForCall) + return; + ASSERT(m_programCodeBlock); + m_programCodeBlock->unlinkCalls(); +#endif +} + +void ProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + ProgramExecutable* thisObject = jsCast<ProgramExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + ScriptExecutable::visitChildren(thisObject, visitor); + if (thisObject->m_programCodeBlock) + thisObject->m_programCodeBlock->visitAggregate(visitor); +} + +void ProgramExecutable::finalize(JSCell* cell) +{ + jsCast<ProgramExecutable*>(cell)->clearCode(); +} + +inline void ProgramExecutable::clearCode() +{ + if (m_programCodeBlock) { + m_programCodeBlock->clearEvalCache(); + m_programCodeBlock.clear(); + } + Base::clearCode(); +} + +FunctionCodeBlock* FunctionExecutable::baselineCodeBlockFor(CodeSpecializationKind kind) +{ + FunctionCodeBlock* result; + if (kind == CodeForCall) + result = m_codeBlockForCall.get(); + else { + ASSERT(kind == CodeForConstruct); + result = m_codeBlockForConstruct.get(); + } + if (!result) + return 0; + while (result->alternative()) + result = static_cast<FunctionCodeBlock*>(result->alternative()); + ASSERT(result); + ASSERT(result->getJITType() == JITCode::BaselineJIT); + return result; +} + +JSObject* FunctionExecutable::compileOptimizedForCall(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + ASSERT(exec->globalData().dynamicGlobalObject); + ASSERT(!!m_codeBlockForCall); + JSObject* error = 0; + if (m_codeBlockForCall->getJITType() != JITCode::topTierJIT()) + error = compileForCallInternal(exec, scopeChainNode, JITCode::nextTierJIT(m_codeBlockForCall->getJITType())); + ASSERT(!!m_codeBlockForCall); + return error; +} + +JSObject* FunctionExecutable::compileOptimizedForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode) +{ + ASSERT(exec->globalData().dynamicGlobalObject); + ASSERT(!!m_codeBlockForConstruct); + JSObject* error = 0; + if (m_codeBlockForConstruct->getJITType() != JITCode::topTierJIT()) + error = compileForConstructInternal(exec, scopeChainNode, JITCode::nextTierJIT(m_codeBlockForConstruct->getJITType())); + ASSERT(!!m_codeBlockForConstruct); + return error; +} + +FunctionCodeBlock* FunctionExecutable::codeBlockWithBytecodeFor(CodeSpecializationKind kind) +{ + FunctionCodeBlock* codeBlock = baselineCodeBlockFor(kind); + if (codeBlock->canProduceCopyWithBytecode()) + return codeBlock; + return 0; +} + +PassOwnPtr<FunctionCodeBlock> FunctionExecutable::produceCodeBlockFor(ScopeChainNode* scopeChainNode, CompilationKind compilationKind, CodeSpecializationKind specializationKind, JSObject*& exception) +{ + if (!!codeBlockFor(specializationKind) && codeBlockFor(specializationKind)->canProduceCopyWithBytecode()) { + BytecodeDestructionBlocker blocker(codeBlockFor(specializationKind).get()); + return adoptPtr(new FunctionCodeBlock(CodeBlock::CopyParsedBlock, *codeBlockFor(specializationKind))); + } + + exception = 0; + JSGlobalData* globalData = scopeChainNode->globalData; + JSGlobalObject* globalObject = scopeChainNode->globalObject.get(); + RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(globalData, globalObject, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, FunctionBodyNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, 0, 0, &exception); + + if (!body) { + ASSERT(exception); + return nullptr; + } + if (m_forceUsesArguments) + body->setUsesArguments(); + body->finishParsing(m_parameters, m_name); + recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); + + OwnPtr<FunctionCodeBlock> result; + ASSERT((compilationKind == FirstCompilation) == !codeBlockFor(specializationKind)); + result = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), specializationKind == CodeForConstruct)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChainNode, result->symbolTable(), result.get(), compilationKind))); + exception = generator->generate(); + body->destroyData(); + if (exception) + return nullptr; + + result->copyPostParseDataFrom(codeBlockFor(specializationKind).get()); + return result.release(); +} + +JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChainNode* scopeChainNode, JITCode::JITType jitType) +{ + SamplingRegion samplingRegion(jitType == JITCode::BaselineJIT ? "Baseline Compilation (TOTAL)" : "DFG Compilation (TOTAL)"); + +#if !ENABLE(JIT) + UNUSED_PARAM(exec); + UNUSED_PARAM(jitType); + UNUSED_PARAM(exec); +#endif + ASSERT((jitType == JITCode::bottomTierJIT()) == !m_codeBlockForCall); + JSObject* exception; + OwnPtr<FunctionCodeBlock> newCodeBlock = produceCodeBlockFor(scopeChainNode, !!m_codeBlockForCall ? OptimizingCompilation : FirstCompilation, CodeForCall, exception); + if (!newCodeBlock) + return exception; + + newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_codeBlockForCall.release())); + m_codeBlockForCall = newCodeBlock.release(); + + m_numParametersForCall = m_codeBlockForCall->m_numParameters; + ASSERT(m_numParametersForCall); + m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars; + m_symbolTable = m_codeBlockForCall->sharedSymbolTable(); + +#if ENABLE(JIT) + JSGlobalData* globalData = scopeChainNode->globalData; + if (globalData->canUseJIT()) { + bool dfgCompiled = false; + if (jitType == JITCode::DFGJIT) + dfgCompiled = DFG::tryCompileFunction(exec, m_codeBlockForCall.get(), m_jitCodeForCall, m_jitCodeForCallWithArityCheck); + if (dfgCompiled) { + if (m_codeBlockForCall->alternative()) + m_codeBlockForCall->alternative()->unlinkIncomingCalls(); + } else { + if (m_codeBlockForCall->alternative()) { + m_codeBlockForCall = static_pointer_cast<FunctionCodeBlock>(m_codeBlockForCall->releaseAlternative()); + m_symbolTable = m_codeBlockForCall->sharedSymbolTable(); + return 0; + } + m_jitCodeForCall = JIT::compile(globalData, m_codeBlockForCall.get(), &m_jitCodeForCallWithArityCheck); + } +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_codeBlockForCall->handleBytecodeDiscardingOpportunity(); +#endif + + m_codeBlockForCall->setJITCode(m_jitCodeForCall, m_jitCodeForCallWithArityCheck); + } +#endif + +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (!m_jitCodeForCall) + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall)); + else +#endif + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall) + m_jitCodeForCall.size()); +#else + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall)); +#endif + + return 0; +} + +JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode, JITCode::JITType jitType) +{ + SamplingRegion samplingRegion(jitType == JITCode::BaselineJIT ? "Baseline Compilation (TOTAL)" : "DFG Compilation (TOTAL)"); + +#if !ENABLE(JIT) + UNUSED_PARAM(jitType); + UNUSED_PARAM(exec); +#endif + + ASSERT((jitType == JITCode::bottomTierJIT()) == !m_codeBlockForConstruct); + JSObject* exception; + OwnPtr<FunctionCodeBlock> newCodeBlock = produceCodeBlockFor(scopeChainNode, !!m_codeBlockForConstruct ? OptimizingCompilation : FirstCompilation, CodeForConstruct, exception); + if (!newCodeBlock) + return exception; + + newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_codeBlockForConstruct.release())); + m_codeBlockForConstruct = newCodeBlock.release(); + + m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters; + ASSERT(m_numParametersForConstruct); + m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars; + m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable(); + +#if ENABLE(JIT) + JSGlobalData* globalData = scopeChainNode->globalData; + if (globalData->canUseJIT()) { + bool dfgCompiled = false; + if (jitType == JITCode::DFGJIT) + dfgCompiled = DFG::tryCompileFunction(exec, m_codeBlockForConstruct.get(), m_jitCodeForConstruct, m_jitCodeForConstructWithArityCheck); + if (dfgCompiled) { + if (m_codeBlockForConstruct->alternative()) + m_codeBlockForConstruct->alternative()->unlinkIncomingCalls(); + } else { + if (m_codeBlockForConstruct->alternative()) { + m_codeBlockForConstruct = static_pointer_cast<FunctionCodeBlock>(m_codeBlockForConstruct->releaseAlternative()); + m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable(); + return 0; + } + m_jitCodeForConstruct = JIT::compile(globalData, m_codeBlockForConstruct.get(), &m_jitCodeForConstructWithArityCheck); + } +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_codeBlockForConstruct->handleBytecodeDiscardingOpportunity(); +#endif + + m_codeBlockForConstruct->setJITCode(m_jitCodeForConstruct, m_jitCodeForConstructWithArityCheck); + } +#endif + +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (!m_jitCodeForConstruct) + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct)); + else +#endif + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct) + m_jitCodeForConstruct.size()); +#else + Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct)); +#endif + + return 0; +} + +#if ENABLE(JIT) +void FunctionExecutable::jettisonOptimizedCodeForCall(JSGlobalData& globalData) +{ + jettisonCodeBlock(globalData, m_codeBlockForCall); + m_jitCodeForCall = m_codeBlockForCall->getJITCode(); + m_jitCodeForCallWithArityCheck = m_codeBlockForCall->getJITCodeWithArityCheck(); +} + +void FunctionExecutable::jettisonOptimizedCodeForConstruct(JSGlobalData& globalData) +{ + jettisonCodeBlock(globalData, m_codeBlockForConstruct); + m_jitCodeForConstruct = m_codeBlockForConstruct->getJITCode(); + m_jitCodeForConstructWithArityCheck = m_codeBlockForConstruct->getJITCodeWithArityCheck(); +} +#endif + +void FunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + FunctionExecutable* thisObject = jsCast<FunctionExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + ScriptExecutable::visitChildren(thisObject, visitor); + if (thisObject->m_nameValue) + visitor.append(&thisObject->m_nameValue); + if (thisObject->m_codeBlockForCall) + thisObject->m_codeBlockForCall->visitAggregate(visitor); + if (thisObject->m_codeBlockForConstruct) + thisObject->m_codeBlockForConstruct->visitAggregate(visitor); +} + +void FunctionExecutable::discardCode() +{ +#if ENABLE(JIT) + // These first two checks are to handle the rare case where + // we are trying to evict code for a function during its + // codegen. + if (!m_jitCodeForCall && m_codeBlockForCall) + return; + if (!m_jitCodeForConstruct && m_codeBlockForConstruct) + return; +#endif + clearCode(); +} + +void FunctionExecutable::finalize(JSCell* cell) +{ + jsCast<FunctionExecutable*>(cell)->clearCode(); +} + +inline void FunctionExecutable::clearCode() +{ + if (m_codeBlockForCall) { + m_codeBlockForCall->clearEvalCache(); + m_codeBlockForCall.clear(); + } + if (m_codeBlockForConstruct) { + m_codeBlockForConstruct->clearEvalCache(); + m_codeBlockForConstruct.clear(); + } + Base::clearCode(); +} + +void FunctionExecutable::unlinkCalls() +{ +#if ENABLE(JIT) + if (!!m_jitCodeForCall) { + ASSERT(m_codeBlockForCall); + m_codeBlockForCall->unlinkCalls(); + } + if (!!m_jitCodeForConstruct) { + ASSERT(m_codeBlockForConstruct); + m_codeBlockForConstruct->unlinkCalls(); + } +#endif +} + +FunctionExecutable* FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception) +{ + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> program = parse<ProgramNode>(&exec->globalData(), lexicalGlobalObject, source, 0, JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, debugger, exec, 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 000000000..cc44d61a7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -0,0 +1,716 @@ +/* + * 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; + + enum CodeSpecializationKind { CodeForCall, CodeForConstruct }; + enum CompilationKind { FirstCompilation, OptimizingCompilation }; + + inline bool isCall(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return true; + ASSERT(kind == CodeForConstruct); + return false; + } + + class ExecutableBase : public JSCell { + friend class JIT; + + protected: + static const int NUM_PARAMETERS_IS_HOST = 0; + static const int NUM_PARAMETERS_NOT_COMPILED = -1; + + ExecutableBase(JSGlobalData& globalData, Structure* structure, int numParameters) + : JSCell(globalData, structure) + , m_numParametersForCall(numParameters) + , m_numParametersForConstruct(numParameters) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + } + + public: + typedef JSCell Base; + + static void destroy(JSCell*); + + bool isHostFunction() const + { + ASSERT((m_numParametersForCall == NUM_PARAMETERS_IS_HOST) == (m_numParametersForConstruct == NUM_PARAMETERS_IS_HOST)); + return m_numParametersForCall == NUM_PARAMETERS_IS_HOST; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(CompoundType, StructureFlags), &s_info); } + + static const ClassInfo s_info; + + protected: + static const unsigned StructureFlags = 0; + 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; + } + + JITCode& generatedJITCodeFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return generatedJITCodeForCall(); + ASSERT(kind == CodeForConstruct); + return generatedJITCodeForConstruct(); + } + + MacroAssemblerCodePtr generatedJITCodeForCallWithArityCheck() + { + ASSERT(m_jitCodeForCall); + ASSERT(m_jitCodeForCallWithArityCheck); + return m_jitCodeForCallWithArityCheck; + } + + MacroAssemblerCodePtr generatedJITCodeForConstructWithArityCheck() + { + ASSERT(m_jitCodeForConstruct); + ASSERT(m_jitCodeForConstructWithArityCheck); + return m_jitCodeForConstructWithArityCheck; + } + + MacroAssemblerCodePtr generatedJITCodeWithArityCheckFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return generatedJITCodeForCallWithArityCheck(); + ASSERT(kind == CodeForConstruct); + return generatedJITCodeForConstructWithArityCheck(); + } + + bool hasJITCodeForCall() const + { + return m_numParametersForCall >= 0; + } + + bool hasJITCodeForConstruct() const + { + return m_numParametersForConstruct >= 0; + } + + bool hasJITCodeFor(CodeSpecializationKind kind) const + { + if (kind == CodeForCall) + return hasJITCodeForCall(); + ASSERT(kind == CodeForConstruct); + return hasJITCodeForConstruct(); + } + + // Intrinsics are only for calls, currently. + Intrinsic intrinsic() const; + + Intrinsic intrinsicFor(CodeSpecializationKind kind) const + { + if (isCall(kind)) + return intrinsic(); + return NoIntrinsic; + } + + protected: + JITCode m_jitCodeForCall; + JITCode m_jitCodeForConstruct; + MacroAssemblerCodePtr m_jitCodeForCallWithArityCheck; + MacroAssemblerCodePtr m_jitCodeForConstructWithArityCheck; +#endif + void clearCode(); + }; + + class NativeExecutable : public ExecutableBase { + friend class JIT; + public: + typedef ExecutableBase Base; + +#if ENABLE(JIT) + static NativeExecutable* create(JSGlobalData& globalData, MacroAssemblerCodeRef callThunk, NativeFunction function, MacroAssemblerCodeRef constructThunk, NativeFunction constructor, Intrinsic intrinsic) + { + ASSERT(globalData.canUseJIT()); + NativeExecutable* executable; + if (!callThunk) { + executable = new (NotNull, allocateCell<NativeExecutable>(globalData.heap)) NativeExecutable(globalData, function, constructor); + executable->finishCreation(globalData, JITCode(), JITCode(), intrinsic); + } else { + executable = new (NotNull, allocateCell<NativeExecutable>(globalData.heap)) NativeExecutable(globalData, function, constructor); + executable->finishCreation(globalData, JITCode::HostFunction(callThunk), JITCode::HostFunction(constructThunk), intrinsic); + } + globalData.heap.addFinalizer(executable, &finalize); + return executable; + } +#endif + +#if ENABLE(INTERPRETER) + static NativeExecutable* create(JSGlobalData& globalData, NativeFunction function, NativeFunction constructor) + { + ASSERT(!globalData.canUseJIT()); + NativeExecutable* executable = new (NotNull, allocateCell<NativeExecutable>(globalData.heap)) NativeExecutable(globalData, function, constructor); + executable->finishCreation(globalData); + globalData.heap.addFinalizer(executable, &finalize); + return executable; + } +#endif + + static void destroy(JSCell*); + + NativeFunction function() { return m_function; } + NativeFunction constructor() { return m_constructor; } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(LeafType, StructureFlags), &s_info); } + + static const ClassInfo s_info; + + Intrinsic intrinsic() const; + + protected: +#if ENABLE(JIT) + void finishCreation(JSGlobalData& globalData, JITCode callThunk, JITCode constructThunk, Intrinsic intrinsic) + { + ASSERT(globalData.canUseJIT()); + Base::finishCreation(globalData); + m_jitCodeForCall = callThunk; + m_jitCodeForConstruct = constructThunk; + m_jitCodeForCallWithArityCheck = callThunk.addressForCall(); + m_jitCodeForConstructWithArityCheck = constructThunk.addressForCall(); + m_intrinsic = intrinsic; + } +#endif + +#if ENABLE(INTERPRETER) + void finishCreation(JSGlobalData& globalData) + { + ASSERT(!globalData.canUseJIT()); + Base::finishCreation(globalData); + m_intrinsic = NoIntrinsic; + } +#endif + + static void finalize(JSCell*); + + private: + NativeExecutable(JSGlobalData& globalData, NativeFunction function, NativeFunction constructor) + : ExecutableBase(globalData, globalData.nativeExecutableStructure.get(), NUM_PARAMETERS_IS_HOST) + , m_function(function) + , m_constructor(constructor) + { + } + + NativeFunction m_function; + NativeFunction m_constructor; + + Intrinsic m_intrinsic; + }; + + class ScriptExecutable : public ExecutableBase { + public: + typedef ExecutableBase Base; + + ScriptExecutable(Structure* structure, JSGlobalData& globalData, const SourceCode& source, bool isInStrictContext) + : ExecutableBase(globalData, structure, NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(isInStrictContext ? StrictModeFeature : 0) + { + } + + ScriptExecutable(Structure* structure, ExecState* exec, const SourceCode& source, bool isInStrictContext) + : ExecutableBase(exec->globalData(), structure, NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(isInStrictContext ? StrictModeFeature : 0) + { + } + + static void destroy(JSCell*); + + 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; } + + void unlinkCalls(); + + static const ClassInfo s_info; + + protected: + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); +#if ENABLE(CODEBLOCK_SAMPLING) + if (SamplingTool* sampler = globalData.interpreter->sampler()) + sampler->notifyOfScope(globalData, this); +#endif + } + + 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: + typedef ScriptExecutable Base; + + static void destroy(JSCell*); + + JSObject* compile(ExecState* exec, ScopeChainNode* scopeChainNode) + { + ASSERT(exec->globalData().dynamicGlobalObject); + JSObject* error = 0; + if (!m_evalCodeBlock) + error = compileInternal(exec, scopeChainNode, JITCode::bottomTierJIT()); + ASSERT(!error == !!m_evalCodeBlock); + return error; + } + + JSObject* compileOptimized(ExecState*, ScopeChainNode*); + +#if ENABLE(JIT) + void jettisonOptimizedCode(JSGlobalData&); +#endif + + EvalCodeBlock& generatedBytecode() + { + ASSERT(m_evalCodeBlock); + return *m_evalCodeBlock; + } + + static EvalExecutable* create(ExecState* exec, const SourceCode& source, bool isInStrictContext) + { + EvalExecutable* executable = new (NotNull, allocateCell<EvalExecutable>(*exec->heap())) EvalExecutable(exec, source, isInStrictContext); + executable->finishCreation(exec->globalData()); + exec->globalData().heap.addFinalizer(executable, &finalize); + return executable; + } + +#if ENABLE(JIT) + JITCode& generatedJITCode() + { + return generatedJITCodeForCall(); + } +#endif + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(EvalExecutableType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + void unlinkCalls(); + + protected: + void clearCode(); + static void finalize(JSCell*); + + private: + static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags; + EvalExecutable(ExecState*, const SourceCode&, bool); + + JSObject* compileInternal(ExecState*, ScopeChainNode*, JITCode::JITType); + static void visitChildren(JSCell*, SlotVisitor&); + + OwnPtr<EvalCodeBlock> m_evalCodeBlock; + }; + + class ProgramExecutable : public ScriptExecutable { + public: + typedef ScriptExecutable Base; + + static ProgramExecutable* create(ExecState* exec, const SourceCode& source) + { + ProgramExecutable* executable = new (NotNull, allocateCell<ProgramExecutable>(*exec->heap())) ProgramExecutable(exec, source); + executable->finishCreation(exec->globalData()); + exec->globalData().heap.addFinalizer(executable, &finalize); + return executable; + } + + static void destroy(JSCell*); + + JSObject* compile(ExecState* exec, ScopeChainNode* scopeChainNode) + { + ASSERT(exec->globalData().dynamicGlobalObject); + JSObject* error = 0; + if (!m_programCodeBlock) + error = compileInternal(exec, scopeChainNode, JITCode::bottomTierJIT()); + ASSERT(!error == !!m_programCodeBlock); + return error; + } + + JSObject* compileOptimized(ExecState*, ScopeChainNode*); + +#if ENABLE(JIT) + void jettisonOptimizedCode(JSGlobalData&); +#endif + + ProgramCodeBlock& generatedBytecode() + { + ASSERT(m_programCodeBlock); + return *m_programCodeBlock; + } + + JSObject* checkSyntax(ExecState*); + +#if ENABLE(JIT) + JITCode& generatedJITCode() + { + return generatedJITCodeForCall(); + } +#endif + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(ProgramExecutableType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + void unlinkCalls(); + + protected: + void clearCode(); + static void finalize(JSCell*); + + private: + static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags; + ProgramExecutable(ExecState*, const SourceCode&); + + JSObject* compileInternal(ExecState*, ScopeChainNode*, JITCode::JITType); + static void visitChildren(JSCell*, SlotVisitor&); + + OwnPtr<ProgramCodeBlock> m_programCodeBlock; + }; + + class FunctionExecutable : public ScriptExecutable { + friend class JIT; + public: + typedef ScriptExecutable Base; + + static FunctionExecutable* create(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool isInStrictContext, int firstLine, int lastLine) + { + FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(*exec->heap())) FunctionExecutable(exec, name, source, forceUsesArguments, parameters, isInStrictContext); + executable->finishCreation(exec->globalData(), name, firstLine, lastLine); + exec->globalData().heap.addFinalizer(executable, &finalize); + return executable; + } + + static FunctionExecutable* create(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool isInStrictContext, int firstLine, int lastLine) + { + FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(globalData.heap)) FunctionExecutable(globalData, name, source, forceUsesArguments, parameters, isInStrictContext); + executable->finishCreation(globalData, name, firstLine, lastLine); + globalData.heap.addFinalizer(executable, &finalize); + return executable; + } + + static void destroy(JSCell*); + + JSFunction* make(ExecState* exec, ScopeChainNode* scopeChain) + { + return JSFunction::create(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; + } + + FunctionCodeBlock* codeBlockWithBytecodeFor(CodeSpecializationKind); + + PassOwnPtr<FunctionCodeBlock> produceCodeBlockFor(ScopeChainNode*, CompilationKind, CodeSpecializationKind, JSObject*& exception); + + JSObject* compileForCall(ExecState* exec, ScopeChainNode* scopeChainNode) + { + ASSERT(exec->globalData().dynamicGlobalObject); + JSObject* error = 0; + if (!m_codeBlockForCall) + error = compileForCallInternal(exec, scopeChainNode, JITCode::bottomTierJIT()); + ASSERT(!error == !!m_codeBlockForCall); + return error; + } + + JSObject* compileOptimizedForCall(ExecState*, ScopeChainNode*); + +#if ENABLE(JIT) + void jettisonOptimizedCodeForCall(JSGlobalData&); +#endif + + bool isGeneratedForCall() const + { + return m_codeBlockForCall; + } + + FunctionCodeBlock& generatedBytecodeForCall() + { + ASSERT(m_codeBlockForCall); + return *m_codeBlockForCall; + } + + JSObject* compileForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode) + { + ASSERT(exec->globalData().dynamicGlobalObject); + JSObject* error = 0; + if (!m_codeBlockForConstruct) + error = compileForConstructInternal(exec, scopeChainNode, JITCode::bottomTierJIT()); + ASSERT(!error == !!m_codeBlockForConstruct); + return error; + } + + JSObject* compileOptimizedForConstruct(ExecState*, ScopeChainNode*); + +#if ENABLE(JIT) + void jettisonOptimizedCodeForConstruct(JSGlobalData&); +#endif + + bool isGeneratedForConstruct() const + { + return m_codeBlockForConstruct; + } + + FunctionCodeBlock& generatedBytecodeForConstruct() + { + ASSERT(m_codeBlockForConstruct); + return *m_codeBlockForConstruct; + } + + JSObject* compileFor(ExecState* exec, ScopeChainNode* scopeChainNode, CodeSpecializationKind kind) + { + ASSERT(exec->callee()); + ASSERT(exec->callee()->inherits(&JSFunction::s_info)); + ASSERT(asFunction(exec->callee())->jsExecutable() == this); + + if (kind == CodeForCall) + return compileForCall(exec, scopeChainNode); + ASSERT(kind == CodeForConstruct); + return compileForConstruct(exec, scopeChainNode); + } + + JSObject* compileOptimizedFor(ExecState* exec, ScopeChainNode* scopeChainNode, CodeSpecializationKind kind) + { + ASSERT(exec->callee()); + ASSERT(exec->callee()->inherits(&JSFunction::s_info)); + ASSERT(asFunction(exec->callee())->jsExecutable() == this); + + if (kind == CodeForCall) + return compileOptimizedForCall(exec, scopeChainNode); + ASSERT(kind == CodeForConstruct); + return compileOptimizedForConstruct(exec, scopeChainNode); + } + +#if ENABLE(JIT) + void jettisonOptimizedCodeFor(JSGlobalData& globalData, CodeSpecializationKind kind) + { + if (kind == CodeForCall) + jettisonOptimizedCodeForCall(globalData); + else { + ASSERT(kind == CodeForConstruct); + jettisonOptimizedCodeForConstruct(globalData); + } + } +#endif + + bool isGeneratedFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return isGeneratedForCall(); + ASSERT(kind == CodeForConstruct); + return isGeneratedForConstruct(); + } + + FunctionCodeBlock& generatedBytecodeFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return generatedBytecodeForCall(); + ASSERT(kind == CodeForConstruct); + return generatedBytecodeForConstruct(); + } + + FunctionCodeBlock* baselineCodeBlockFor(CodeSpecializationKind); + + FunctionCodeBlock* profiledCodeBlockFor(CodeSpecializationKind kind) + { + return baselineCodeBlockFor(kind); + } + + const Identifier& name() { return m_name; } + JSString* nameValue() const { return m_nameValue.get(); } + size_t parameterCount() const { return m_parameters->size(); } // Excluding 'this'! + unsigned capturedVariableCount() const { return m_numCapturedVariables; } + UString paramString() const; + SharedSymbolTable* symbolTable() const { return m_symbolTable; } + + void discardCode(); + static void visitChildren(JSCell*, SlotVisitor&); + static FunctionExecutable* fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, JSObject** exception); + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(FunctionExecutableType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + void unlinkCalls(); + + protected: + void clearCode(); + static void finalize(JSCell*); + + void finishCreation(JSGlobalData& globalData, const Identifier& name, int firstLine, int lastLine) + { + Base::finishCreation(globalData); + m_firstLine = firstLine; + m_lastLine = lastLine; + m_nameValue.set(globalData, this, jsString(&globalData, name.ustring())); + } + + private: + FunctionExecutable(JSGlobalData&, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool); + FunctionExecutable(ExecState*, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool); + + JSObject* compileForCallInternal(ExecState*, ScopeChainNode*, JITCode::JITType); + JSObject* compileForConstructInternal(ExecState*, ScopeChainNode*, JITCode::JITType); + + OwnPtr<FunctionCodeBlock>& codeBlockFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return m_codeBlockForCall; + ASSERT(kind == CodeForConstruct); + return m_codeBlockForConstruct; + } + + static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags; + unsigned m_numCapturedVariables : 31; + bool m_forceUsesArguments : 1; + + RefPtr<FunctionParameters> m_parameters; + OwnPtr<FunctionCodeBlock> m_codeBlockForCall; + OwnPtr<FunctionCodeBlock> m_codeBlockForConstruct; + Identifier m_name; + WriteBarrier<JSString> m_nameValue; + SharedSymbolTable* m_symbolTable; + }; + + 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(); + } + + inline NativeFunction JSFunction::nativeFunction() + { + ASSERT(isHostFunction()); + return static_cast<NativeExecutable*>(m_executable.get())->function(); + } + + inline NativeFunction JSFunction::nativeConstructor() + { + ASSERT(isHostFunction()); + return static_cast<NativeExecutable*>(m_executable.get())->constructor(); + } + + inline bool isHostFunction(JSValue value, NativeFunction nativeFunction) + { + JSFunction* function = static_cast<JSFunction*>(getJSFunction(value)); + if (!function || !function->isHostFunction()) + return false; + return function->nativeFunction() == nativeFunction; + } + + inline void ScriptExecutable::unlinkCalls() + { + switch (structure()->typeInfo().type()) { + case EvalExecutableType: + return jsCast<EvalExecutable*>(this)->unlinkCalls(); + case ProgramExecutableType: + return jsCast<ProgramExecutable*>(this)->unlinkCalls(); + case FunctionExecutableType: + return jsCast<FunctionExecutable*>(this)->unlinkCalls(); + default: + ASSERT_NOT_REACHED(); + } + } + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp new file mode 100644 index 000000000..e08e58c83 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -0,0 +1,133 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(FunctionConstructor); + +const ClassInfo FunctionConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionConstructor) }; + +FunctionConstructor::FunctionConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void FunctionConstructor::finishCreation(ExecState* exec, FunctionPrototype* functionPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, functionPrototype->classInfo()->className)); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, functionPrototype, DontEnum | DontDelete | ReadOnly); + + // Number of arguments for constructor + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +static EncodedJSValue JSC_HOST_CALL constructWithFunctionConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructFunction(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +ConstructType FunctionConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithFunctionConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callFunctionConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructFunction(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +// ECMA 15.3.1 The Function Constructor Called as a Function +CallType FunctionConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callFunctionConstructor; + return CallTypeHost; +} + +// ECMA 15.3.2 The Function Constructor +JSObject* constructFunction(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, const Identifier& functionName, const UString& sourceURL, const TextPosition& position) +{ + if (!globalObject->evalEnabled()) + return throwError(exec, createEvalError(exec, "Function constructor is disabled")); + return constructFunctionSkippingEvalEnabledCheck(exec, globalObject, args, functionName, sourceURL, position); +} + +JSObject* constructFunctionSkippingEvalEnabledCheck(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, const Identifier& functionName, const UString& sourceURL, const TextPosition& position) +{ + // 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(); + } + + JSGlobalData& globalData = globalObject->globalData(); + SourceCode source = makeSource(program, sourceURL, position); + JSObject* exception = 0; + FunctionExecutable* function = FunctionExecutable::fromGlobalCode(functionName, exec, exec->dynamicGlobalObject()->debugger(), source, &exception); + if (!function) { + ASSERT(exception); + return throwError(exec, exception); + } + + ScopeChainNode* scopeChain = ScopeChainNode::create(exec, 0, globalObject, &globalData, globalObject, exec->globalThisValue()); + return JSFunction::create(exec, function, scopeChain); +} + +// ECMA 15.3.2 The Function Constructor +JSObject* constructFunction(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args) +{ + return constructFunction(exec, globalObject, args, Identifier(exec, "anonymous"), UString(), TextPosition::minimumPosition()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.h b/Source/JavaScriptCore/runtime/FunctionConstructor.h new file mode 100644 index 000000000..7141916cf --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.h @@ -0,0 +1,66 @@ +/* + * 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 WTF { +class TextPosition; +} + +namespace JSC { + + class FunctionPrototype; + + class FunctionConstructor : public InternalFunction { + public: + typedef InternalFunction Base; + + static FunctionConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, FunctionPrototype* functionPrototype) + { + FunctionConstructor* constructor = new (NotNull, allocateCell<FunctionConstructor>(*exec->heap())) FunctionConstructor(globalObject, structure); + constructor->finishCreation(exec, functionPrototype); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + private: + FunctionConstructor(JSGlobalObject*, Structure*); + void finishCreation(ExecState*, FunctionPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + }; + + JSObject* constructFunction(ExecState*, JSGlobalObject*, const ArgList&, const Identifier& functionName, const UString& sourceURL, const WTF::TextPosition&); + JSObject* constructFunction(ExecState*, JSGlobalObject*, const ArgList&); + + JSObject* constructFunctionSkippingEvalEnabledCheck(ExecState*, JSGlobalObject*, const ArgList&, const Identifier&, const UString&, const WTF::TextPosition&); + +} // namespace JSC + +#endif // FunctionConstructor_h diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp new file mode 100644 index 000000000..049b7b914 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -0,0 +1,215 @@ +/* + * 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 "JSBoundFunction.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Interpreter.h" +#include "Lexer.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(FunctionPrototype); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(FunctionPrototype); + +const ClassInfo FunctionPrototype::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionPrototype) }; + +static EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState*); + +FunctionPrototype::FunctionPrototype(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void FunctionPrototype::finishCreation(ExecState* exec, const Identifier& name) +{ + Base::finishCreation(exec->globalData(), name); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction) +{ + JSFunction* toStringFunction = JSFunction::create(exec, globalObject, 0, exec->propertyNames().toString, functionProtoFuncToString); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().toString, toStringFunction, DontEnum); + + *applyFunction = JSFunction::create(exec, globalObject, 2, exec->propertyNames().apply, functionProtoFuncApply); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().apply, *applyFunction, DontEnum); + + *callFunction = JSFunction::create(exec, globalObject, 1, exec->propertyNames().call, functionProtoFuncCall); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().call, *callFunction, DontEnum); + + JSFunction* bindFunction = JSFunction::create(exec, globalObject, 1, exec->propertyNames().bind, functionProtoFuncBind); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().bind, bindFunction, DontEnum); +} + +static EncodedJSValue JSC_HOST_CALL callFunctionPrototype(ExecState*) +{ + return JSValue::encode(jsUndefined()); +} + +// ECMA 15.3.4 +CallType FunctionPrototype::getCallData(JSCell*, 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<UChar>::isWhiteSpace(ch) && !Lexer<UChar>::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::s_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::s_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::s_info) { + if (asArguments(array)->length(exec) > Arguments::MaxArguments) + return JSValue::encode(throwStackOverflowError(exec)); + asArguments(array)->fillArgList(exec, applyArgs); + } else if (isJSArray(array)) { + if (asArray(array)->length() > Arguments::MaxArguments) + return JSValue::encode(throwStackOverflowError(exec)); + asArray(array)->fillArgList(exec, applyArgs); + } else { + unsigned length = asObject(array)->get(exec, exec->propertyNames().length).toUInt32(exec); + if (length > Arguments::MaxArguments) + return JSValue::encode(throwStackOverflowError(exec)); + + for (unsigned i = 0; i < length; ++i) + applyArgs.append(asObject(array)->get(exec, i)); + } + } + + 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)); +} + +// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]]) +EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec) +{ + JSGlobalObject* globalObject = exec->callee()->globalObject(); + + // Let Target be the this value. + JSValue target = exec->hostThisValue(); + + // If IsCallable(Target) is false, throw a TypeError exception. + CallData callData; + CallType callType = getCallData(target, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + // Primitive values are not callable. + ASSERT(target.isObject()); + JSObject* targetObject = asObject(target); + + // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order. + size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0; + JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->globalData(), globalObject->arrayStructure(), numBoundArgs); + if (!boundArgs) + return JSValue::encode(throwOutOfMemoryError(exec)); + + for (size_t i = 0; i < numBoundArgs; ++i) + boundArgs->initializeIndex(exec->globalData(), i, exec->argument(i + 1)); + boundArgs->completeInitialization(numBoundArgs); + + // If the [[Class]] internal property of Target is "Function", then ... + // Else set the length own property of F to 0. + unsigned length = 0; + if (targetObject->inherits(&JSFunction::s_info)) { + ASSERT(target.get(exec, exec->propertyNames().length).isNumber()); + // a. Let L be the length property of Target minus the length of A. + // b. Set the length own property of F to either 0 or L, whichever is larger. + unsigned targetLength = (unsigned)target.get(exec, exec->propertyNames().length).asNumber(); + if (targetLength > numBoundArgs) + length = targetLength - numBoundArgs; + } + + Identifier name(exec, target.get(exec, exec->propertyNames().name).toString(exec)); + + return JSValue::encode(JSBoundFunction::create(exec, globalObject, targetObject, exec->argument(0), boundArgs, length, name)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.h b/Source/JavaScriptCore/runtime/FunctionPrototype.h new file mode 100644 index 000000000..7dd1c2418 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.h @@ -0,0 +1,58 @@ +/* + * 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 FunctionPrototype : public InternalFunction { + public: + typedef InternalFunction Base; + + static FunctionPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + FunctionPrototype* prototype = new (NotNull, allocateCell<FunctionPrototype>(*exec->heap())) FunctionPrototype(globalObject, structure); + prototype->finishCreation(exec, exec->propertyNames().nullIdentifier); + return prototype; + } + + void addFunctionProperties(ExecState*, JSGlobalObject*, JSFunction** callFunction, JSFunction** applyFunction); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + protected: + void finishCreation(ExecState*, const Identifier& name); + + private: + FunctionPrototype(JSGlobalObject*, Structure*); + static CallType getCallData(JSCell*, 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 000000000..308d245a9 --- /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*) +{ +} + +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 000000000..f40ebee5b --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCActivityCallback.h @@ -0,0 +1,82 @@ +/* + * 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> + +#if USE(CF) +#include <CoreFoundation/CoreFoundation.h> +#endif + +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(); + +#if USE(CF) +protected: + DefaultGCActivityCallback(Heap*, CFRunLoopRef); + void commonConstructor(Heap*, CFRunLoopRef); +#endif + +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 000000000..2e878bfe3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp @@ -0,0 +1,110 @@ +/* + * 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 "Heap.h" +#include "JSGlobalData.h" +#include "JSLock.h" +#include "JSObject.h" +#include "ScopeChain.h" +#include <wtf/RetainPtr.h> +#include <wtf/WTFThreadData.h> + +#if !USE(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 timer, void *info) +{ + Heap* heap = static_cast<Heap*>(info); + APIEntryShim shim(heap->globalData()); + heap->collectAllGarbage(); + CFRunLoopTimerSetNextFireDate(timer, CFAbsoluteTimeGetCurrent() + decade); +} + +DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap) +{ + commonConstructor(heap, CFRunLoopGetCurrent()); +} + +DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap, CFRunLoopRef runLoop) +{ + commonConstructor(heap, runLoop); +} + +DefaultGCActivityCallback::~DefaultGCActivityCallback() +{ + CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); + CFRunLoopTimerInvalidate(d->timer.get()); + d->context.info = 0; + d->runLoop = 0; + d->timer = 0; +} + +void DefaultGCActivityCallback::commonConstructor(Heap* heap, CFRunLoopRef runLoop) +{ + d = adoptPtr(new DefaultGCActivityCallbackPlatformData); + + memset(&d->context, 0, sizeof(CFRunLoopTimerContext)); + d->context.info = heap; + d->runLoop = runLoop; + d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::trigger, &d->context)); + CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); +} + +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/GetterSetter.cpp b/Source/JavaScriptCore/runtime/GetterSetter.cpp new file mode 100644 index 000000000..920399dda --- /dev/null +++ b/Source/JavaScriptCore/runtime/GetterSetter.cpp @@ -0,0 +1,48 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(GetterSetter); + +const ClassInfo GetterSetter::s_info = { "GetterSetter", 0, 0, 0, CREATE_METHOD_TABLE(GetterSetter) }; + +void GetterSetter::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + GetterSetter* thisObject = jsCast<GetterSetter*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + JSCell::visitChildren(thisObject, visitor); + + if (thisObject->m_getter) + visitor.append(&thisObject->m_getter); + if (thisObject->m_setter) + visitor.append(&thisObject->m_setter); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/GetterSetter.h b/Source/JavaScriptCore/runtime/GetterSetter.h new file mode 100644 index 000000000..293bf6434 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GetterSetter.h @@ -0,0 +1,85 @@ +/* + * 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" +#include "Structure.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; + + private: + GetterSetter(ExecState* exec) + : JSCell(exec->globalData(), exec->globalData().getterSetterStructure.get()) + { + } + + public: + typedef JSCell Base; + + static GetterSetter* create(ExecState* exec) + { + GetterSetter* getterSetter = new (NotNull, allocateCell<GetterSetter>(*exec->heap())) GetterSetter(exec); + getterSetter->finishCreation(exec->globalData()); + return getterSetter; + } + + static void visitChildren(JSCell*, SlotVisitor&); + + JSObject* getter() const { return m_getter.get(); } + void setGetter(JSGlobalData& globalData, JSObject* getter) { m_getter.set(globalData, this, getter); } + JSObject* setter() const { return m_setter.get(); } + void setSetter(JSGlobalData& globalData, JSObject* setter) { m_setter.set(globalData, this, setter); } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(GetterSetterType, OverridesVisitChildren), &s_info); + } + + static const ClassInfo s_info; + + private: + WriteBarrier<JSObject> m_getter; + WriteBarrier<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/Identifier.cpp b/Source/JavaScriptCore/runtime/Identifier.cpp new file mode 100644 index 000000000..0d233e355 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Identifier.cpp @@ -0,0 +1,288 @@ +/* + * 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 "JSObject.h" +#include "NumericStrings.h" +#include "ScopeChain.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/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; +} + +IdentifierTable* createIdentifierTable() +{ + return new IdentifierTable; +} + +void deleteIdentifierTable(IdentifierTable* table) +{ + delete table; +} + +struct IdentifierCStringTranslator { + static unsigned hash(const LChar* c) + { + return StringHasher::computeHash<LChar>(c); + } + + static bool equal(StringImpl* r, const LChar* s) + { + return Identifier::equal(r, s); + } + + static void translate(StringImpl*& location, const LChar* c, unsigned hash) + { + size_t length = strlen(reinterpret_cast<const char*>(c)); + LChar* d; + StringImpl* r = StringImpl::createUninitialized(length, d).leakRef(); + for (size_t i = 0; i != length; i++) + d[i] = c[i]; + r->setHash(hash); + location = r; + } +}; + +struct IdentifierLCharFromUCharTranslator { + static unsigned hash(const CharBuffer<UChar>& buf) + { + return StringHasher::computeHash<UChar>(buf.s, buf.length); + } + + static bool equal(StringImpl* str, const CharBuffer<UChar>& buf) + { + return Identifier::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, const CharBuffer<UChar>& buf, unsigned hash) + { + LChar* d; + StringImpl* r = StringImpl::createUninitialized(buf.length, d).leakRef(); + for (unsigned i = 0; i != buf.length; i++) { + UChar c = buf.s[i]; + ASSERT(c <= 0xff); + d[i] = c; + } + 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(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 LChar*, IdentifierCStringTranslator>(reinterpret_cast<const LChar*>(c)); + + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + RefPtr<StringImpl> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; + + literalIdentifierTable.add(c, addedString.get()); + + return addedString.release(); +} + +PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const char* c) +{ + return add(&exec->globalData(), c); +} + +PassRefPtr<StringImpl> Identifier::add8(JSGlobalData* globalData, const UChar* s, int length) +{ + if (length == 1) { + UChar c = s[0]; + ASSERT(c <= 0xff); + if (canUseSingleCharacterString(c)) + return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); + } + + if (!length) + return StringImpl::empty(); + CharBuffer<UChar> buf = {s, length}; + pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<CharBuffer<UChar>, IdentifierLCharFromUCharTranslator >(buf); + + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + return addResult.second ? adoptRef(*addResult.first) : *addResult.first; +} + +template <typename CharType> +ALWAYS_INLINE uint32_t Identifier::toUInt32FromCharacters(const CharType* characters, unsigned length, bool& ok) +{ + // 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; +} + +uint32_t Identifier::toUInt32(const UString& string, bool& ok) +{ + ok = false; + + unsigned length = string.length(); + + // An empty string is not a number. + if (!length) + return 0; + + if (string.is8Bit()) + return toUInt32FromCharacters(string.characters8(), length, ok); + return toUInt32FromCharacters(string.characters16(), length, ok); +} + +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)[0]; + if (c <= maxSingleCharacterString) + 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! +NO_RETURN_DUE_TO_CRASH void Identifier::checkCurrentIdentifierTable(JSGlobalData*) { CRASH(); } +NO_RETURN_DUE_TO_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 000000000..2cc88b256 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Identifier.h @@ -0,0 +1,258 @@ +/* + * 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/WTFThreadData.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, 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 LChar* s, int length) : m_string(add(globalData, s, length)) { } + 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 createLCharFromUChar(JSGlobalData* globalData, const UChar* s, int length) { return Identifier(globalData, add8(globalData, s, length)); } + + 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 LChar*); + friend bool operator==(const Identifier&, const char*); + friend bool operator!=(const Identifier&, const LChar*); + friend bool operator!=(const Identifier&, const char*); + + static bool equal(const StringImpl*, const LChar*); + static inline bool equal(const StringImpl*a, const char*b) { return Identifier::equal(a, reinterpret_cast<const LChar*>(b)); }; + static bool equal(const StringImpl*, const LChar*, unsigned length); + 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; + + template <typename CharType> + ALWAYS_INLINE static uint32_t toUInt32FromCharacters(const CharType* characters, unsigned length, bool& ok); + + 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 LChar* b) { return equal(a.m_string.impl(), b); } + + template <typename T> static PassRefPtr<StringImpl> add(JSGlobalData*, const T*, int length); + static PassRefPtr<StringImpl> add8(JSGlobalData*, const UChar*, int length); + template <typename T> ALWAYS_INLINE static bool canUseSingleCharacterString(T); + + 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*); + }; + + template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(LChar) + { + ASSERT(maxSingleCharacterString == 0xff); + return true; + } + + template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(UChar c) + { + return (c <= maxSingleCharacterString); + } + + template <typename T> + struct CharBuffer { + const T* s; + unsigned int length; + }; + + template <typename T> + struct IdentifierCharBufferTranslator { + static unsigned hash(const CharBuffer<T>& buf) + { + return StringHasher::computeHash<T>(buf.s, buf.length); + } + + static bool equal(StringImpl* str, const CharBuffer<T>& buf) + { + return Identifier::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, const CharBuffer<T>& buf, unsigned hash) + { + T* 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; + } + }; + + template <typename T> + PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const T* s, int length) + { + if (length == 1) { + T c = s[0]; + if (canUseSingleCharacterString(c)) + return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); + } + + if (!length) + return StringImpl::empty(); + CharBuffer<T> buf = {s, length}; + pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<CharBuffer<T>, IdentifierCharBufferTranslator<T> >(buf); + + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + return addResult.second ? adoptRef(*addResult.first) : *addResult.first; + } + + 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 LChar* b) + { + return Identifier::equal(a, b); + } + + inline bool operator==(const Identifier& a, const char* b) + { + return Identifier::equal(a, reinterpret_cast<const LChar*>(b)); + } + + inline bool operator!=(const Identifier& a, const LChar* b) + { + return !Identifier::equal(a, b); + } + + inline bool operator!=(const Identifier& a, const char* b) + { + return !Identifier::equal(a, reinterpret_cast<const LChar*>(b)); + } + + inline bool Identifier::equal(const StringImpl* r, const LChar* s) + { + return WTF::equal(r, s); + } + + inline bool Identifier::equal(const StringImpl* r, const LChar* s, unsigned length) + { + return WTF::equal(r, s, length); + } + + inline bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length) + { + return WTF::equal(r, s, length); + } + + 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(); } + }; + + struct IdentifierMapIndexHashTraits : HashTraits<int> { + static int emptyValue() { return std::numeric_limits<int>::max(); } + static const bool emptyValueIsZero = false; + }; + + typedef HashMap<RefPtr<StringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<StringImpl> >, IdentifierMapIndexHashTraits> IdentifierMap; + + template<typename U, typename V> + std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(U value) + { + std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add<U, V>(value); + (*result.first)->setIsIdentifier(true); + return result; + } + +} // namespace JSC + +#endif // Identifier_h diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp new file mode 100644 index 000000000..2b874c708 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -0,0 +1,79 @@ +/* + * 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 "ExecutableAllocator.h" +#include "Heap.h" +#include "Options.h" +#include "Identifier.h" +#include "JSDateMath.h" +#include "JSGlobalObject.h" +#include "UString.h" +#include "WriteBarrier.h" +#include "dtoa.h" +#include <wtf/Threading.h> +#include <wtf/dtoa/cached-powers.h> + +using namespace WTF; + +namespace JSC { + +#if OS(DARWIN) +static pthread_once_t initializeThreadingKeyOnce = PTHREAD_ONCE_INIT; +#endif + +static void initializeThreadingOnce() +{ + WTF::double_conversion::initialize(); + WTF::initializeThreading(); + Options::initializeOptions(); +#if ENABLE(WRITE_BARRIER_PROFILING) + WriteBarrierCounters::initialize(); +#endif +#if ENABLE(JIT) && ENABLE(ASSEMBLER) + ExecutableAllocator::initializeAllocator(); +#endif + RegisterFile::initializeThreading(); +} + +void initializeThreading() +{ +#if OS(DARWIN) + 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 000000000..1a93ccb9b --- /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 000000000..50ea504c1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InternalFunction.cpp @@ -0,0 +1,81 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(InternalFunction); + +const ClassInfo InternalFunction::s_info = { "Function", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(InternalFunction) }; + +InternalFunction::InternalFunction(JSGlobalObject* globalObject, Structure* structure) + : JSNonFinalObject(globalObject->globalData(), structure) +{ +} + +void InternalFunction::finishCreation(JSGlobalData& globalData, const Identifier& name) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + ASSERT(methodTable()->getCallData != InternalFunction::s_info.methodTable.getCallData); + putDirect(globalData, globalData.propertyNames->name, jsString(&globalData, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); +} + +const UString& InternalFunction::name(ExecState* exec) +{ + return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue(); +} + +const UString InternalFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName); + + if (displayName && isJSString(displayName)) + return asString(displayName)->tryGetValue(); + + return UString(); +} + +CallType InternalFunction::getCallData(JSCell*, CallData&) +{ + ASSERT_NOT_REACHED(); + return CallTypeNone; +} + +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 000000000..a038b7ae1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InternalFunction.h @@ -0,0 +1,69 @@ +/* + * 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 "JSObject.h" +#include "Identifier.h" + +namespace JSC { + + class FunctionPrototype; + + class InternalFunction : public JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + static JS_EXPORTDATA const ClassInfo s_info; + + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + static const unsigned StructureFlags = ImplementsHasInstance | JSObject::StructureFlags; + + InternalFunction(JSGlobalObject*, Structure*); + + void finishCreation(JSGlobalData&, const Identifier& name); + + static CallType getCallData(JSCell*, CallData&); + }; + + InternalFunction* asInternalFunction(JSValue); + + inline InternalFunction* asInternalFunction(JSValue value) + { + ASSERT(asObject(value)->inherits(&InternalFunction::s_info)); + return static_cast<InternalFunction*>(asObject(value)); + } + +} // namespace JSC + +#endif // InternalFunction_h diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h new file mode 100644 index 000000000..5cc00685f --- /dev/null +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 Intrinsic_h +#define Intrinsic_h + +namespace JSC { + +enum Intrinsic { + NoIntrinsic, + AbsIntrinsic, + MinIntrinsic, + MaxIntrinsic, + SqrtIntrinsic, + ArrayPushIntrinsic, + ArrayPopIntrinsic, + CharCodeAtIntrinsic, + CharAtIntrinsic, + FromCharCodeIntrinsic, + PowIntrinsic, + FloorIntrinsic, + CeilIntrinsic, + RoundIntrinsic, + ExpIntrinsic, + LogIntrinsic, +}; + +} // namespace JSC + +#endif // Intrinsic_h diff --git a/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp new file mode 100644 index 000000000..8517085df --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp @@ -0,0 +1,35 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSAPIValueWrapper); + +const ClassInfo JSAPIValueWrapper::s_info = { "API Wrapper", 0, 0, 0, CREATE_METHOD_TABLE(JSAPIValueWrapper) }; + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h new file mode 100644 index 000000000..21d7b215c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.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, 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 "JSValue.h" +#include "CallFrame.h" +#include "Structure.h" + +namespace JSC { + + class JSAPIValueWrapper : public JSCell { + friend JSValue jsAPIValueWrapper(ExecState*, JSValue); + public: + typedef JSCell Base; + + JSValue value() const { return m_value.get(); } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(APIValueWrapperType, OverridesVisitChildren | OverridesGetPropertyNames), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; + + static JSAPIValueWrapper* create(ExecState* exec, JSValue value) + { + JSAPIValueWrapper* wrapper = new (NotNull, allocateCell<JSAPIValueWrapper>(*exec->heap())) JSAPIValueWrapper(exec); + wrapper->finishCreation(exec, value); + return wrapper; + } + + protected: + void finishCreation(ExecState* exec, JSValue value) + { + Base::finishCreation(exec->globalData()); + m_value.set(exec->globalData(), this, value); + ASSERT(!value.isCell()); + } + + private: + JSAPIValueWrapper(ExecState* exec) + : JSCell(exec->globalData(), exec->globalData().apiWrapperStructure.get()) + { + } + + WriteBarrier<Unknown> m_value; + }; + + inline JSValue jsAPIValueWrapper(ExecState* exec, JSValue value) + { + return JSAPIValueWrapper::create(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 000000000..83d1ee493 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSActivation.cpp @@ -0,0 +1,244 @@ +/* + * 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" + +using namespace std; + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSActivation); + +const ClassInfo JSActivation::s_info = { "JSActivation", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSActivation) }; + +JSActivation::JSActivation(CallFrame* callFrame, FunctionExecutable* functionExecutable) + : Base(callFrame->globalData(), callFrame->globalData().activationStructure.get(), functionExecutable->symbolTable(), callFrame->registers()) + , m_numCapturedArgs(max(callFrame->argumentCount(), functionExecutable->parameterCount())) + , m_numCapturedVars(functionExecutable->capturedVariableCount()) + , m_requiresDynamicChecks(functionExecutable->usesEval()) + , m_argumentsRegister(functionExecutable->generatedBytecode().argumentsRegister()) +{ +} + +void JSActivation::finishCreation(CallFrame* callFrame) +{ + Base::finishCreation(callFrame->globalData()); + ASSERT(inherits(&s_info)); + + // We have to manually ref and deref the symbol table as JSVariableObject + // doesn't know about SharedSymbolTable + static_cast<SharedSymbolTable*>(m_symbolTable)->ref(); + callFrame->globalData().heap.addFinalizer(this, &finalize); +} + +void JSActivation::finalize(JSCell* cell) +{ + static_cast<SharedSymbolTable*>(jsCast<JSActivation*>(cell)->m_symbolTable)->deref(); +} + +void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSActivation* thisObject = jsCast<JSActivation*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); + + // No need to mark our registers if they're still in the RegisterFile. + WriteBarrier<Unknown>* registerArray = thisObject->m_registerArray.get(); + if (!registerArray) + return; + + visitor.appendValues(registerArray, thisObject->m_numCapturedArgs); + + // Skip 'this' and call frame. + visitor.appendValues(registerArray + CallFrame::offsetFor(thisObject->m_numCapturedArgs + 1), thisObject->m_numCapturedVars); +} + +inline bool JSActivation::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) +{ + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (entry.isNull()) + return false; + if (entry.getIndex() >= m_numCapturedVars) + return false; + + slot.setValue(registerAt(entry.getIndex()).get()); + return true; +} + +inline bool JSActivation::symbolTablePut(ExecState* exec, const Identifier& propertyName, JSValue value, bool shouldThrow) +{ + JSGlobalData& globalData = exec->globalData(); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (entry.isNull()) + return false; + if (entry.isReadOnly()) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return true; + } + if (entry.getIndex() >= m_numCapturedVars) + return false; + + registerAt(entry.getIndex()).set(globalData, this, value); + return true; +} + +void JSActivation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSActivation* thisObject = jsCast<JSActivation*>(object); + SymbolTable::const_iterator end = thisObject->symbolTable().end(); + for (SymbolTable::const_iterator it = thisObject->symbolTable().begin(); it != end; ++it) { + if (it->second.getAttributes() & DontEnum && mode != IncludeDontEnumProperties) + continue; + if (it->second.getIndex() >= thisObject->m_numCapturedVars) + continue; + propertyNames.add(Identifier(exec, it->first.get())); + } + // Skip the JSVariableObject implementation of getOwnPropertyNames + JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); +} + +inline bool JSActivation::symbolTablePutWithAttributes(JSGlobalData& globalData, 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() >= m_numCapturedVars) + return false; + + entry.setAttributes(attributes); + registerAt(entry.getIndex()).set(globalData, this, value); + return true; +} + +bool JSActivation::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSActivation* thisObject = jsCast<JSActivation*>(cell); + if (propertyName == exec->propertyNames().arguments) { + slot.setCustom(thisObject, thisObject->getArgumentsGetter()); + return true; + } + + if (thisObject->symbolTableGet(propertyName, slot)) + return true; + + if (WriteBarrierBase<Unknown>* location = thisObject->getDirectLocation(exec->globalData(), propertyName)) { + slot.setValue(location->get()); + 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(!thisObject->hasGetterSetterProperties()); + ASSERT(thisObject->prototype().isNull()); + return false; +} + +void JSActivation::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSActivation* thisObject = jsCast<JSActivation*>(cell); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + + if (thisObject->symbolTablePut(exec, propertyName, value, slot.isStrictMode())) + 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(!thisObject->hasGetterSetterProperties()); + thisObject->putDirect(exec->globalData(), propertyName, value, 0, true, slot); +} + +// FIXME: Make this function honor ReadOnly (const) and DontEnum +void JSActivation::putWithAttributes(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + JSActivation* thisObject = jsCast<JSActivation*>(object); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + + if (thisObject->symbolTablePutWithAttributes(exec->globalData(), 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(!thisObject->hasGetterSetterProperties()); + JSObject::putWithAttributes(thisObject, exec, propertyName, value, attributes); +} + +bool JSActivation::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().arguments) + return false; + + return Base::deleteProperty(cell, exec, propertyName); +} + +JSObject* JSActivation::toThisObject(JSCell*, ExecState* exec) +{ + return exec->globalThisValue(); +} + +JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, const Identifier&) +{ + JSActivation* activation = asActivation(slotBase); + CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(activation->m_registers)); + int argumentsRegister = activation->m_argumentsRegister; + if (JSValue arguments = callFrame->uncheckedR(argumentsRegister).jsValue()) + return arguments; + int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister); + + JSValue arguments = JSValue(Arguments::create(callFrame->globalData(), callFrame)); + callFrame->uncheckedR(argumentsRegister) = arguments; + callFrame->uncheckedR(realArgumentsRegister) = arguments; + + ASSERT(callFrame->uncheckedR(realArgumentsRegister).jsValue().inherits(&Arguments::s_info)); + return callFrame->uncheckedR(realArgumentsRegister).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 000000000..2ce053e05 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSActivation.h @@ -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. + */ + +#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 { + private: + JSActivation(CallFrame*, FunctionExecutable*); + + public: + typedef JSVariableObject Base; + + static JSActivation* create(JSGlobalData& globalData, CallFrame* callFrame, FunctionExecutable* funcExec) + { + JSActivation* activation = new (NotNull, allocateCell<JSActivation>(globalData.heap)) JSActivation(callFrame, funcExec); + activation->finishCreation(callFrame); + return activation; + } + + static void finalize(JSCell*); + + static void visitChildren(JSCell*, SlotVisitor&); + + bool isDynamicScope(bool& requiresDynamicChecks) const; + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static void put(JSCell*, ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + static void putWithAttributes(JSObject*, ExecState*, const Identifier&, JSValue, unsigned attributes); + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + + static JSObject* toThisObject(JSCell*, ExecState*); + + void tearOff(JSGlobalData&); + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(ActivationObjectType, StructureFlags), &s_info); } + + protected: + void finishCreation(CallFrame*); + static const unsigned StructureFlags = IsEnvironmentRecord | OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; + + private: + bool symbolTableGet(const Identifier&, PropertySlot&); + bool symbolTableGet(const Identifier&, PropertyDescriptor&); + bool symbolTableGet(const Identifier&, PropertySlot&, bool& slotIsWriteable); + bool symbolTablePut(ExecState*, const Identifier&, JSValue, bool shouldThrow); + bool symbolTablePutWithAttributes(JSGlobalData&, const Identifier&, JSValue, unsigned attributes); + + static JSValue argumentsGetter(ExecState*, JSValue, const Identifier&); + NEVER_INLINE PropertySlot::GetValueFunc getArgumentsGetter(); + + int m_numCapturedArgs; + int m_numCapturedVars : 31; + bool m_requiresDynamicChecks : 1; + int m_argumentsRegister; + }; + + JSActivation* asActivation(JSValue); + + inline JSActivation* asActivation(JSValue value) + { + ASSERT(asObject(value)->inherits(&JSActivation::s_info)); + return static_cast<JSActivation*>(asObject(value)); + } + + ALWAYS_INLINE JSActivation* Register::activation() const + { + return asActivation(jsValue()); + } + + inline bool JSActivation::isDynamicScope(bool& requiresDynamicChecks) const + { + requiresDynamicChecks = m_requiresDynamicChecks; + return false; + } + + inline void JSActivation::tearOff(JSGlobalData& globalData) + { + ASSERT(!m_registerArray); + ASSERT(m_numCapturedVars + m_numCapturedArgs); + + int registerOffset = CallFrame::offsetFor(m_numCapturedArgs + 1); + size_t registerArraySize = registerOffset + m_numCapturedVars; + + OwnArrayPtr<WriteBarrier<Unknown> > registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[registerArraySize]); + WriteBarrier<Unknown>* registers = registerArray.get() + registerOffset; + + // Copy all arguments that can be captured by name or by the arguments object. + for (int i = 0; i < m_numCapturedArgs; ++i) { + int index = CallFrame::argumentOffset(i); + registers[index].set(globalData, this, m_registers[index].get()); + } + + // Skip 'this' and call frame. + + // Copy all captured vars. + for (int i = 0; i < m_numCapturedVars; ++i) + registers[i].set(globalData, this, m_registers[i].get()); + + setRegisters(registers, registerArray.release()); + } + +} // namespace JSC + +#endif // JSActivation_h diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp new file mode 100644 index 000000000..da7be8564 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -0,0 +1,1379 @@ +/* + * 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, +// unless the array is in SparseMode in which case all properties go into the map. +// * 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(WriteBarrier<Unknown>)) + +// (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t). +#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>)) + +// 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::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)}; + +// 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(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>)); + // 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(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)))); + + return size; +} + +static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) +{ + return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; +} + +#if !CHECK_ARRAY_CONSISTENCY + +inline void JSArray::checkConsistency(ConsistencyCheckType) +{ +} + +#endif + +JSArray::JSArray(JSGlobalData& globalData, Structure* structure) + : JSNonFinalObject(globalData, structure) + , m_storage(0) +{ +} + +void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + + unsigned initialVectorLength = BASE_VECTOR_LEN; + unsigned initialStorageSize = storageSize(initialVectorLength); + + m_storage = static_cast<ArrayStorage*>(fastMalloc(initialStorageSize)); + m_storage->m_allocBase = m_storage; + m_storage->m_length = initialLength; + m_indexBias = 0; + m_vectorLength = initialVectorLength; + m_storage->m_sparseValueMap = 0; + m_storage->subclassData = 0; + m_storage->m_numValuesInVector = 0; +#if CHECK_ARRAY_CONSISTENCY + m_storage->m_inCompactInitialization = false; +#endif + + WriteBarrier<Unknown>* vector = m_storage->m_vector; + for (size_t i = 0; i < initialVectorLength; ++i) + vector[i].clear(); + + checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(initialStorageSize); +} + +JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + + // Check for lengths larger than we can handle with a vector. + if (initialLength > MAX_STORAGE_VECTOR_LENGTH) + return 0; + + unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN); + unsigned initialStorageSize = storageSize(initialVectorLength); + + m_storage = static_cast<ArrayStorage*>(fastMalloc(initialStorageSize)); + m_storage->m_allocBase = m_storage; + m_storage->m_length = 0; + m_indexBias = 0; + m_vectorLength = initialVectorLength; + m_storage->m_sparseValueMap = 0; + m_storage->subclassData = 0; + m_storage->m_numValuesInVector = initialLength; +#if CHECK_ARRAY_CONSISTENCY + m_storage->m_inCompactInitialization = true; +#endif + + WriteBarrier<Unknown>* vector = m_storage->m_vector; + for (size_t i = initialLength; i < initialVectorLength; ++i) + vector[i].clear(); + + Heap::heap(this)->reportExtraMemoryCost(initialStorageSize); + return this; +} + +JSArray::~JSArray() +{ + ASSERT(jsCast<JSArray*>(this)); + + // If we are unable to allocate memory for m_storage then this may be null. + if (!m_storage) + return; + + checkConsistency(DestructorConsistencyCheck); + delete m_storage->m_sparseValueMap; + fastFree(m_storage->m_allocBase); +} + +void JSArray::destroy(JSCell* cell) +{ + jsCast<JSArray*>(cell)->JSArray::~JSArray(); +} + +SparseArrayValueMap::iterator SparseArrayValueMap::find(unsigned i) +{ + return m_map.find(i); +} + +inline void SparseArrayValueMap::put(JSGlobalData& globalData, JSArray* array, unsigned i, JSValue value) +{ + SparseArrayEntry temp; + pair<Map::iterator, bool> result = m_map.add(i, temp); + result.first->second.set(globalData, array, value); + if (!result.second) // pre-existing entry + return; + + size_t capacity = m_map.capacity(); + if (capacity != m_reportedCapacity) { + Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); + m_reportedCapacity = capacity; + } +} + +inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor) +{ + iterator end = m_map.end(); + for (iterator it = m_map.begin(); it != end; ++it) + visitor.append(&it->second); +} + +bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + ArrayStorage* storage = thisObject->m_storage; + + if (i >= storage->m_length) { + if (i > MAX_ARRAY_INDEX) + return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); + return false; + } + + if (i < thisObject->m_vectorLength) { + JSValue value = storage->m_vector[i].get(); + if (value) { + slot.setValue(value); + return true; + } + } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->notFound()) { + slot.setValue(it->second.get()); + return true; + } + } + + return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); +} + +bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + if (propertyName == exec->propertyNames().length) { + slot.setValue(jsNumber(thisObject->length())); + return true; + } + + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) + return JSArray::getOwnPropertySlotByIndex(thisObject, exec, i, slot); + + return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSArray* thisObject = jsCast<JSArray*>(object); + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(thisObject->length()), DontDelete | DontEnum); + return true; + } + + ArrayStorage* storage = thisObject->m_storage; + + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) { + if (i >= storage->m_length) + return false; + if (i < thisObject->m_vectorLength) { + WriteBarrier<Unknown>& value = storage->m_vector[i]; + if (value) { + descriptor.setDescriptor(value.get(), 0); + return true; + } + } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->notFound()) { + descriptor.setDescriptor(it->second.get(), 0); + return true; + } + } + } + return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); +} + +// ECMA 15.4.5.1 +void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) { + putByIndex(thisObject, 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; + } + thisObject->setLength(newLength); + return; + } + + JSObject::put(thisObject, exec, propertyName, value, slot); +} + +void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + thisObject->checkConsistency(); + + ArrayStorage* storage = thisObject->m_storage; + + // Fast case - store to the vector. + if (i < thisObject->m_vectorLength) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; + unsigned length = storage->m_length; + + // Update m_length & m_numValuesInVector as necessary. + if (i >= length) { + length = i + 1; + storage->m_length = length; + ++storage->m_numValuesInVector; + } else if (!valueSlot) + ++storage->m_numValuesInVector; + + valueSlot.set(exec->globalData(), thisObject, value); + thisObject->checkConsistency(); + return; + } + + // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property. + if (UNLIKELY(i > MAX_ARRAY_INDEX)) { + PutPropertySlot slot; + thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot); + return; + } + + // For all other cases, call putByIndexBeyondVectorLength. + thisObject->putByIndexBeyondVectorLength(exec->globalData(), i, value); + thisObject->checkConsistency(); +} + +NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(JSGlobalData& globalData, unsigned i, JSValue value) +{ + // i should be a valid array index that is outside of the current vector. + ASSERT(i >= m_vectorLength); + ASSERT(i <= MAX_ARRAY_INDEX); + + ArrayStorage* storage = m_storage; + SparseArrayValueMap* map = storage->m_sparseValueMap; + + // Update m_length if necessary. + unsigned length = storage->m_length; + if (i >= length) { + length = i + 1; + storage->m_length = length; + } + + // First, handle cases where we don't currently have a sparse map. + if (LIKELY(!map)) { + // Check that it is sensible to still be using a vector, and then try to grow the vector. + if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(i + 1))) { + // success! - reread m_storage since it has likely been reallocated, and store to the vector. + storage = m_storage; + storage->m_vector[i].set(globalData, this, value); + ++storage->m_numValuesInVector; + return; + } + // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. + map = new SparseArrayValueMap; + storage->m_sparseValueMap = map; + map->put(globalData, this, i, value); + return; + } + + // We are currently using a map - check whether we still want to be doing so. + // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. + unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); + if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(length)) { + map->put(globalData, this, i, value); + return; + } + + // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector. + storage = m_storage; + storage->m_numValuesInVector = numValuesInArray; + + // Copy all values from the map into the vector, and delete the map. + WriteBarrier<Unknown>* vector = storage->m_vector; + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) + vector[it->first].set(globalData, this, it->second.get()); + delete map; + storage->m_sparseValueMap = 0; + + // Store the new property into the vector. + WriteBarrier<Unknown>& valueSlot = vector[i]; + if (!valueSlot) + ++storage->m_numValuesInVector; + valueSlot.set(globalData, this, value); +} + +bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(isArrayIndex); + if (isArrayIndex) + return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i); + + if (propertyName == exec->propertyNames().length) + return false; + + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + thisObject->checkConsistency(); + + ArrayStorage* storage = thisObject->m_storage; + + if (i < thisObject->m_vectorLength) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; + if (!valueSlot) { + thisObject->checkConsistency(); + return false; + } + valueSlot.clear(); + --storage->m_numValuesInVector; + thisObject->checkConsistency(); + return true; + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->notFound()) { + map->remove(it); + thisObject->checkConsistency(); + return true; + } + } + + thisObject->checkConsistency(); + + if (i > MAX_ARRAY_INDEX) + return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i)); + + return false; +} + +void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSArray* thisObject = jsCast<JSArray*>(object); + // 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 = thisObject->m_storage; + + unsigned usedVectorLength = min(storage->m_length, thisObject->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::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) + propertyNames.add(Identifier::from(exec, it->first)); + } + + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().length); + + JSObject::getOwnPropertyNames(thisObject, 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. + if (newLength > MAX_STORAGE_VECTOR_LENGTH) + return false; + + ArrayStorage* storage = m_storage; + + unsigned vectorLength = m_vectorLength; + ASSERT(newLength > vectorLength); + unsigned newVectorLength = getNewVectorLength(newLength); + void* baseStorage = storage->m_allocBase; + + // Fast case - there is no precapacity. In these cases a realloc makes sense. + if (LIKELY(!m_indexBias)) { + if (!tryFastRealloc(baseStorage, storageSize(newVectorLength)).getValue(baseStorage)) + return false; + + storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(baseStorage); + m_storage->m_allocBase = baseStorage; + + WriteBarrier<Unknown>* vector = storage->m_vector; + for (unsigned i = vectorLength; i < newVectorLength; ++i) + vector[i].clear(); + + m_vectorLength = newVectorLength; + + Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); + return true; + } + + // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. + unsigned newIndexBias = min(m_indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); + // Calculate new stoarge capcity, allowing room for the pre-capacity. + unsigned newStorageCapacity = newVectorLength + newIndexBias; + void* newAllocBase; + if (!tryFastMalloc(storageSize(newStorageCapacity)).getValue(newAllocBase)) + return false; + // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. + ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias); + unsigned currentCapacity = m_vectorLength + m_indexBias; + // Currently there is no way to report to the heap that the extra capacity is shrinking! + if (newStorageCapacity > currentCapacity) + Heap::heap(this)->reportExtraMemoryCost((newStorageCapacity - currentCapacity) * sizeof(WriteBarrier<Unknown>)); + + m_vectorLength = newVectorLength; + m_indexBias = newIndexBias; + m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias); + + // Copy the ArrayStorage header & current contents of the vector, clear the new post-capacity. + memmove(m_storage, storage, storageSize(vectorLength)); + for (unsigned i = vectorLength; i < m_vectorLength; ++i) + m_storage->m_vector[i].clear(); + + // Free the old allocation, update m_allocBase. + fastFree(m_storage->m_allocBase); + m_storage->m_allocBase = newAllocBase; + + return true; +} + +// This method makes room in the vector, but leaves the new space uncleared. +bool JSArray::unshiftCountSlowCase(unsigned count) +{ + // If not, we should have handled this on the fast path. + ASSERT(count > m_indexBias); + + ArrayStorage* storage = m_storage; + + // Step 1: + // Gather 4 key metrics: + // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors). + // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more. + // * currentCapacity - what is the current size of the vector, including any pre-capacity. + // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength. + + unsigned length = storage->m_length; + unsigned usedVectorLength = min(m_vectorLength, length); + ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH); + // Check that required vector length is possible, in an overflow-safe fashion. + if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength) + return false; + unsigned requiredVectorLength = usedVectorLength + count; + ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH); + // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. + ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias); + unsigned currentCapacity = m_vectorLength + m_indexBias; + // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH. + unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1); + + // Step 2: + // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing on. + + void* newAllocBase; + unsigned newStorageCapacity; + // If the current storage array is sufficiently large (but not too large!) then just keep using it. + if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) { + newAllocBase = storage->m_allocBase; + newStorageCapacity = currentCapacity; + } else { + if (!tryFastMalloc(storageSize(desiredCapacity)).getValue(newAllocBase)) + return false; + newStorageCapacity = desiredCapacity; + // Currently there is no way to report to the heap that the extra capacity is shrinking! + if (desiredCapacity > currentCapacity) + Heap::heap(this)->reportExtraMemoryCost((desiredCapacity - currentCapacity) * sizeof(WriteBarrier<Unknown>)); + } + + // Step 3: + // Work out where we're going to move things to. + + // Determine how much of the vector to use as pre-capacity, and how much as post-capacity. + // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any. + // If it did, we calculate the amount that will remain based on an atomic decay - leave the + // vector with half the post-capacity it had previously. + unsigned postCapacity = 0; + if (length < m_vectorLength) { + // Atomic decay, + the post-capacity cannot be greater than what is available. + postCapacity = min((m_vectorLength - length) >> 1, newStorageCapacity - requiredVectorLength); + // If we're moving contents within the same allocation, the post-capacity is being reduced. + ASSERT(newAllocBase != storage->m_allocBase || postCapacity < m_vectorLength - length); + } + + m_vectorLength = requiredVectorLength + postCapacity; + m_indexBias = newStorageCapacity - m_vectorLength; + m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias); + + // Step 4: + // Copy array data / header into their new locations, clear post-capacity & free any old allocation. + + // If this is being moved within the existing buffer of memory, we are always shifting data + // to the right (since count > m_indexBias). As such this memmove cannot trample the header. + memmove(m_storage->m_vector + count, storage->m_vector, sizeof(WriteBarrier<Unknown>) * usedVectorLength); + memmove(m_storage, storage, storageSize(0)); + + // Are we copying into a new allocation? + if (newAllocBase != m_storage->m_allocBase) { + // Free the old allocation, update m_allocBase. + fastFree(m_storage->m_allocBase); + m_storage->m_allocBase = newAllocBase; + + // We need to clear any entries in the vector beyond length. We only need to + // do this if this was a new allocation, because if we're using an existing + // allocation the post-capacity will already be cleared, and in an existing + // allocation we can only beshrinking the amount of post capacity. + for (unsigned i = requiredVectorLength; i < m_vectorLength; ++i) + m_storage->m_vector[i].clear(); + } + + return true; +} + +void JSArray::setLength(unsigned newLength) +{ + checkConsistency(); + + ArrayStorage* storage = m_storage; + + unsigned length = storage->m_length; + + if (newLength < length) { + unsigned usedVectorLength = min(length, m_vectorLength); + for (unsigned i = newLength; i < usedVectorLength; ++i) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; + bool hadValue = valueSlot; + valueSlot.clear(); + storage->m_numValuesInVector -= hadValue; + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap copy = *map; + SparseArrayValueMap::const_iterator end = copy.end(); + for (SparseArrayValueMap::const_iterator it = copy.begin(); it != end; ++it) { + if (it->first >= newLength) + map->remove(it->first); + } + if (map->isEmpty() && !map->sparseMode()) { + 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) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[length]; + if (valueSlot) { + --storage->m_numValuesInVector; + result = valueSlot.get(); + valueSlot.clear(); + } 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.get(); + map->remove(it); + if (map->isEmpty() && !map->sparseMode()) { + delete map; + storage->m_sparseValueMap = 0; + } + } + } + } + + storage->m_length = length; + + checkConsistency(); + + return result; +} + +// Push & putIndex are almost identical, with two small differences. +// - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector. +// - pushing to an array of length 2^32-1 stores the property, but throws a range error. +void JSArray::push(ExecState* exec, JSValue value) +{ + checkConsistency(); + ArrayStorage* storage = m_storage; + + // Fast case - push within vector, always update m_length & m_numValuesInVector. + unsigned length = storage->m_length; + if (length < m_vectorLength) { + storage->m_vector[length].set(exec->globalData(), this, value); + storage->m_length = length + 1; + ++storage->m_numValuesInVector; + checkConsistency(); + return; + } + + // Pushing to an array of length 2^32-1 stores the property, but throws a range error. + if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) { + methodTable()->putByIndex(this, exec, storage->m_length, value); + // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. + throwError(exec, createRangeError(exec, "Invalid array length")); + return; + } + + // Handled the same as putIndex. + putByIndexBeyondVectorLength(exec->globalData(), storage->m_length, value); + checkConsistency(); +} + +void JSArray::shiftCount(ExecState* exec, unsigned 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))) + methodTable()->putByIndex(this, 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(WriteBarrier<Unknown>); + memmove(newBaseStorage, storage, storageSize(0)); + m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); + + m_indexBias += count; + } + } +} + +void JSArray::unshiftCount(ExecState* exec, unsigned count) +{ + ArrayStorage* storage = m_storage; + 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))) + methodTable()->putByIndex(this, 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(WriteBarrier<Unknown>); + memmove(newBaseStorage, storage, storageSize(0)); + m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); + m_vectorLength += count; + } else if (!unshiftCountSlowCase(count)) { + throwOutOfMemoryError(exec); + return; + } + + WriteBarrier<Unknown>* vector = m_storage->m_vector; + for (unsigned i = 0; i < count; i++) + vector[i].clear(); +} + +void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + + JSNonFinalObject::visitChildren(thisObject, visitor); + + ArrayStorage* storage = thisObject->m_storage; + + unsigned usedVectorLength = std::min(storage->m_length, thisObject->m_vectorLength); + visitor.appendValues(storage->m_vector, usedVectorLength); + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) + map->visitChildren(visitor); +} + +static int compareNumbersForQSort(const void* a, const void* b) +{ + double da = static_cast<const JSValue*>(a)->asNumber(); + double db = static_cast<const JSValue*>(b)->asNumber(); + 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) +{ + ASSERT(!inSparseMode()); + + 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(WriteBarrier<Unknown>), compareNumbersForQSort); + + checkConsistency(SortConsistencyCheck); +} + +void JSArray::sort(ExecState* exec) +{ + ASSERT(!inSparseMode()); + + 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].get(); + 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()) { + Heap::heap(this)->popTempSortVector(&values); + 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; + + JSGlobalData& globalData = exec->globalData(); + for (size_t i = 0; i < lengthNotIncludingUndefined; i++) + storage->m_vector[i].set(globalData, this, 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; + 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(jsUndefined()); + 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, jsUndefined(), 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) +{ + ASSERT(!inSparseMode()); + + 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_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].get(); + 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].get(); + 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::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { + tree.abstractor().m_nodes[numDefined].value = it->second.get(); + 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); + JSGlobalData& globalData = exec->globalData(); + for (unsigned i = 0; i < numDefined; ++i) { + storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); + ++iter; + } + + // Put undefined values back in. + for (unsigned i = numDefined; i < newUsedVectorLength; ++i) + storage->m_vector[i].setUndefined(); + + // Ensure that unused values in the vector are zeroed out. + for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) + storage->m_vector[i].clear(); + + storage->m_numValuesInVector = newUsedVectorLength; + + checkConsistency(SortConsistencyCheck); +} + +void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) +{ + ArrayStorage* storage = m_storage; + + WriteBarrier<Unknown>* vector = storage->m_vector; + unsigned vectorEnd = min(storage->m_length, m_vectorLength); + unsigned i = 0; + for (; i < vectorEnd; ++i) { + WriteBarrier<Unknown>& v = vector[i]; + if (!v) + break; + args.append(v.get()); + } + + for (; i < storage->m_length; ++i) + args.append(get(exec, i)); +} + +void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) +{ + ASSERT(length == this->length()); + UNUSED_PARAM(length); + unsigned i = 0; + WriteBarrier<Unknown>* vector = m_storage->m_vector; + unsigned vectorEnd = min(length, m_vectorLength); + for (; i < vectorEnd; ++i) { + WriteBarrier<Unknown>& v = vector[i]; + if (!v) + break; + callFrame->setArgument(i, v.get()); + } + + for (; i < length; ++i) + callFrame->setArgument(i, get(exec, i)); +} + +unsigned JSArray::compactForSorting() +{ + ASSERT(!inSparseMode()); + + 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].get(); + if (!v || v.isUndefined()) + break; + } + + for (unsigned i = numDefined; i < usedVectorLength; ++i) { + JSValue v = storage->m_vector[i].get(); + if (v) { + if (v.isUndefined()) + ++numUndefined; + else + storage->m_vector[numDefined++].setWithoutWriteBarrier(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::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) + storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.get()); + + delete map; + storage->m_sparseValueMap = 0; + } + + for (unsigned i = numDefined; i < newUsedVectorLength; ++i) + storage->m_vector[i].setUndefined(); + for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) + storage->m_vector[i].clear(); + + 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->m_inCompactInitialization); + + 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 000000000..9102c8724 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -0,0 +1,293 @@ +/* + * 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 { + + class JSArray; + + struct SparseArrayEntry : public WriteBarrier<Unknown> { + SparseArrayEntry() : attributes(0) {} + unsigned attributes; + }; + + class SparseArrayValueMap { + typedef HashMap<unsigned, SparseArrayEntry> Map; + + enum Flags { + Normal = 0, + SparseMode = 1 + }; + + public: + typedef Map::iterator iterator; + typedef Map::const_iterator const_iterator; + + SparseArrayValueMap() + : m_flags(Normal) + , m_reportedCapacity(0) + { + } + + void visitChildren(SlotVisitor&); + + bool sparseMode() + { + return m_flags & SparseMode; + } + + void setSparseMode() + { + m_flags = (Flags)(m_flags | SparseMode); + } + + // These methods may mutate the contents of the map + void put(JSGlobalData&, JSArray*, unsigned, JSValue); + iterator find(unsigned); + // This should ASSERT the remove is valid (check the result of the find). + void remove(iterator it) { m_map.remove(it); } + void remove(unsigned i) { m_map.remove(i); } + + // These methods do not mutate the contents of the map. + iterator notFound() { return m_map.end(); } + bool isEmpty() const { return m_map.isEmpty(); } + bool contains(unsigned i) const { return m_map.contains(i); } + size_t size() const { return m_map.size(); } + // Only allow const begin/end iteration. + const_iterator begin() const { return m_map.begin(); } + const_iterator end() const { return m_map.end(); } + + private: + Map m_map; + Flags m_flags; + size_t m_reportedCapacity; + }; + + // 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. +#if CHECK_ARRAY_CONSISTENCY + bool m_inCompactInitialization; +#endif + WriteBarrier<Unknown> m_vector[1]; + }; + + class JSArray : public JSNonFinalObject { + friend class Walker; + + protected: + explicit JSArray(JSGlobalData&, Structure*); + + void finishCreation(JSGlobalData&, unsigned initialLength = 0); + JSArray* tryFinishCreationUninitialized(JSGlobalData&, unsigned initialLength); + + public: + typedef JSNonFinalObject Base; + + ~JSArray(); + static void destroy(JSCell*); + + static JSArray* create(JSGlobalData& globalData, Structure* structure, unsigned initialLength = 0) + { + JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure); + array->finishCreation(globalData, initialLength); + return array; + } + + // tryCreateUninitialized is used for fast construction of arrays whose size and + // contents are known at time of creation. Clients of this interface must: + // - null-check the result (indicating out of memory, or otherwise unable to allocate vector). + // - call 'initializeIndex' for all properties in sequence, for 0 <= i < initialLength. + // - called 'completeInitialization' after all properties have been initialized. + static JSArray* tryCreateUninitialized(JSGlobalData& globalData, Structure* structure, unsigned initialLength) + { + JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure); + return array->tryFinishCreationUninitialized(globalData, initialLength); + } + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + + static JS_EXPORTDATA const ClassInfo s_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*, unsigned count); + void unshiftCount(ExecState*, unsigned 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].get(); + } + + bool canSetIndex(unsigned i) { return i < m_vectorLength; } + void setIndex(JSGlobalData& globalData, unsigned i, JSValue v) + { + ASSERT(canSetIndex(i)); + + WriteBarrier<Unknown>& 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.set(globalData, this, v); + } + + inline void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v) + { + ASSERT(canSetIndex(i)); + ArrayStorage *storage = m_storage; +#if CHECK_ARRAY_CONSISTENCY + ASSERT(storage->m_inCompactInitialization); +#endif + // Check that we are initializing the next index in sequence. + ASSERT_UNUSED(i, i == storage->m_length); + // tryCreateUninitialized set m_numValuesInVector to the initialLength, + // check we do not try to initialize more than this number of properties. + ASSERT(storage->m_length < storage->m_numValuesInVector); + // It is improtant that we increment length here, so that all newly added + // values in the array still get marked during the initialization phase. + storage->m_vector[storage->m_length++].set(globalData, this, v); + } + + inline void completeInitialization(unsigned newLength) + { + // Check that we have initialized as meny properties as we think we have. + ASSERT_UNUSED(newLength, newLength == m_storage->m_length); + // Check that the number of propreties initialized matches the initialLength. + ASSERT(m_storage->m_length == m_storage->m_numValuesInVector); +#if CHECK_ARRAY_CONSISTENCY + ASSERT(m_storage->m_inCompactInitialization); + m_storage->m_inCompactInitialization = false; +#endif + } + + bool inSparseMode() + { + SparseArrayValueMap* map = m_storage->m_sparseValueMap; + return map && map->sparseMode(); + } + + void fillArgList(ExecState*, MarkedArgumentBuffer&); + void copyToArguments(ExecState*, CallFrame*, uint32_t length); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static ptrdiff_t storageOffset() + { + return OBJECT_OFFSETOF(JSArray, m_storage); + } + + static ptrdiff_t vectorLengthOffset() + { + return OBJECT_OFFSETOF(JSArray, m_vectorLength); + } + + static void visitChildren(JSCell*, SlotVisitor&); + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + void* subclassData() const; + void setSubclassData(void*); + + private: + bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&); + void putByIndexBeyondVectorLength(JSGlobalData&, unsigned propertyName, JSValue); + + unsigned getNewVectorLength(unsigned desiredLength); + bool increaseVectorLength(unsigned newLength); + bool unshiftCountSlowCase(unsigned count); + + unsigned compactForSorting(); + + enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck }; + void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck); + + unsigned m_vectorLength; // The valid length of m_vector + unsigned 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::s_info)); + return static_cast<JSArray*>(cell); + } + + inline JSArray* asArray(JSValue value) + { + return asArray(value.asCell()); + } + + inline bool isJSArray(JSCell* cell) { return cell->classInfo() == &JSArray::s_info; } + inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); } + + // 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/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp new file mode 100644 index 000000000..549b6034f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 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 "JSBoundFunction.h" + +#include "JSGlobalObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSBoundFunction); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSBoundFunction); + +const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; + +EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) +{ + JSBoundFunction* boundFunction = static_cast<JSBoundFunction*>(exec->callee()); + + ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! + JSArray* boundArgs = asArray(boundFunction->boundArgs()); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < boundArgs->length(); ++i) + args.append(boundArgs->getIndex(i)); + for (unsigned i = 0; i < exec->argumentCount(); ++i) + args.append(exec->argument(i)); + + JSObject* targetFunction = boundFunction->targetFunction(); + CallData callData; + CallType callType = getCallData(targetFunction, callData); + ASSERT(callType != CallTypeNone); + return JSValue::encode(call(exec, targetFunction, callType, callData, boundFunction->boundThis(), args)); +} + +EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec) +{ + JSBoundFunction* boundFunction = static_cast<JSBoundFunction*>(exec->callee()); + + ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! + JSArray* boundArgs = asArray(boundFunction->boundArgs()); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < boundArgs->length(); ++i) + args.append(boundArgs->getIndex(i)); + for (unsigned i = 0; i < exec->argumentCount(); ++i) + args.append(exec->argument(i)); + + JSObject* targetFunction = boundFunction->targetFunction(); + ConstructData constructData; + ConstructType constructType = getConstructData(targetFunction, constructData); + ASSERT(constructType != ConstructTypeNone); + return JSValue::encode(construct(exec, targetFunction, constructType, constructData, args)); +} + +JSBoundFunction* JSBoundFunction::create(ExecState* exec, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int length, const Identifier& name) +{ + ConstructData constructData; + ConstructType constructType = JSC::getConstructData(targetFunction, constructData); + bool canConstruct = constructType != ConstructTypeNone; + + NativeExecutable* executable = exec->globalData().getHostFunction(boundFunctionCall, canConstruct ? boundFunctionConstruct : callHostFunctionAsConstructor); + JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(*exec->heap())) JSBoundFunction(exec, globalObject, globalObject->boundFunctionStructure(), targetFunction, boundThis, boundArgs); + + function->finishCreation(exec, executable, length, name); + return function; +} + +bool JSBoundFunction::hasInstance(JSObject* object, ExecState* exec, JSValue value, JSValue) +{ + JSBoundFunction* thisObject = jsCast<JSBoundFunction*>(object); + // FIXME: our instanceof implementation will have already (incorrectly) performed + // a [[Get]] of .prototype from the bound function object, which is incorrect! + // https://bugs.webkit.org/show_bug.cgi?id=68656 + JSValue proto = thisObject->m_targetFunction->get(exec, exec->propertyNames().prototype); + return thisObject->m_targetFunction->methodTable()->hasInstance(thisObject->m_targetFunction.get(), exec, value, proto); +} + +JSBoundFunction::JSBoundFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs) + : Base(exec, globalObject, structure) + , m_targetFunction(exec->globalData(), this, targetFunction) + , m_boundThis(exec->globalData(), this, boundThis) + , m_boundArgs(exec->globalData(), this, boundArgs) +{ +} + +void JSBoundFunction::finishCreation(ExecState* exec, NativeExecutable* executable, int length, const Identifier& name) +{ + Base::finishCreation(exec, executable, length, name); + ASSERT(inherits(&s_info)); + + initializeGetterSetterProperty(exec, exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); + initializeGetterSetterProperty(exec, exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); +} + +void JSBoundFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSBoundFunction* thisObject = jsCast<JSBoundFunction*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_targetFunction); + visitor.append(&thisObject->m_boundThis); + visitor.append(&thisObject->m_boundArgs); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.h b/Source/JavaScriptCore/runtime/JSBoundFunction.h new file mode 100644 index 000000000..c60d7db01 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 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 JSBoundFunction_h +#define JSBoundFunction_h + +#include "JSFunction.h" + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState*); +EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState*); + +class JSBoundFunction : public JSFunction { +public: + typedef JSFunction Base; + + static JSBoundFunction* create(ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const Identifier&); + + static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue proto); + + JSObject* targetFunction() { return m_targetFunction.get(); } + JSValue boundThis() { return m_boundThis.get(); } + JSValue boundArgs() { return m_boundArgs.get(); } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(globalData, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; + +protected: + const static unsigned StructureFlags = OverridesHasInstance | OverridesVisitChildren | Base::StructureFlags; + + static void visitChildren(JSCell*, SlotVisitor&); + +private: + JSBoundFunction(ExecState*, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs); + + void finishCreation(ExecState*, NativeExecutable*, int, const Identifier&); + + WriteBarrier<JSObject> m_targetFunction; + WriteBarrier<Unknown> m_boundThis; + WriteBarrier<Unknown> m_boundArgs; +}; + +} // namespace JSC + +#endif // JSFunction_h diff --git a/Source/JavaScriptCore/runtime/JSByteArray.cpp b/Source/JavaScriptCore/runtime/JSByteArray.cpp new file mode 100644 index 000000000..7478a08fb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSByteArray.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSByteArray.h" + +#include "JSGlobalObject.h" +#include "PropertyNameArray.h" + +using namespace WTF; + +namespace JSC { + +const ClassInfo JSByteArray::s_info = { "ByteArray", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSByteArray) }; + +JSByteArray::JSByteArray(ExecState* exec, Structure* structure, ByteArray* storage) + : JSNonFinalObject(exec->globalData(), structure) + , m_storage(storage) +{ +} + +JSByteArray::~JSByteArray() +{ + ASSERT(jsCast<JSByteArray*>(this)); +} + +void JSByteArray::destroy(JSCell* cell) +{ + jsCast<JSByteArray*>(cell)->JSByteArray::~JSByteArray(); +} + +Structure* JSByteArray::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const JSC::ClassInfo* classInfo) +{ + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), classInfo); +} + +bool JSByteArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSByteArray* thisObject = jsCast<JSByteArray*>(cell); + bool ok; + unsigned index = propertyName.toUInt32(ok); + if (ok && thisObject->canAccessIndex(index)) { + slot.setValue(thisObject->getIndex(exec, index)); + return true; + } + return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +bool JSByteArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSByteArray* thisObject = jsCast<JSByteArray*>(object); + bool ok; + unsigned index = propertyName.toUInt32(ok); + if (ok && thisObject->canAccessIndex(index)) { + descriptor.setDescriptor(thisObject->getIndex(exec, index), DontDelete); + return true; + } + return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); +} + +bool JSByteArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSByteArray* thisObject = jsCast<JSByteArray*>(cell); + if (thisObject->canAccessIndex(propertyName)) { + slot.setValue(thisObject->getIndex(exec, propertyName)); + return true; + } + return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); +} + +void JSByteArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSByteArray* thisObject = jsCast<JSByteArray*>(cell); + bool ok; + unsigned index = propertyName.toUInt32(ok); + if (ok) { + thisObject->setIndex(exec, index, value); + return; + } + JSObject::put(thisObject, exec, propertyName, value, slot); +} + +void JSByteArray::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value) +{ + jsCast<JSByteArray*>(cell)->setIndex(exec, propertyName, value); +} + +void JSByteArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSByteArray* thisObject = jsCast<JSByteArray*>(object); + unsigned length = thisObject->m_storage->length(); + for (unsigned i = 0; i < length; ++i) + propertyNames.add(Identifier::from(exec, i)); + JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); +} + +} + diff --git a/Source/JavaScriptCore/runtime/JSByteArray.h b/Source/JavaScriptCore/runtime/JSByteArray.h new file mode 100644 index 000000000..754774d3e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSByteArray.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSByteArray_h +#define JSByteArray_h + +#include "JSObject.h" + +#include <wtf/ByteArray.h> + +namespace JSC { + + class JSByteArray : public JSNonFinalObject { + friend class JSGlobalData; + public: + typedef JSNonFinalObject Base; + + bool canAccessIndex(unsigned i) { return i < m_storage->length(); } + JSValue getIndex(ExecState*, unsigned i) + { + ASSERT(canAccessIndex(i)); + return jsNumber(m_storage->data()[i]); + } + + void setIndex(unsigned i, int value) + { + ASSERT(canAccessIndex(i)); + if (value & ~0xFF) { + if (value < 0) + value = 0; + else + value = 255; + } + m_storage->data()[i] = static_cast<unsigned char>(value); + } + + void setIndex(unsigned i, double value) + { + ASSERT(canAccessIndex(i)); + if (!(value > 0)) // Clamp NaN to 0 + value = 0; + else if (value > 255) + value = 255; + m_storage->data()[i] = static_cast<unsigned char>(value + 0.5); + } + + void setIndex(ExecState* exec, unsigned i, JSValue value) + { + double byteValue = value.toNumber(exec); + if (exec->hadException()) + return; + if (canAccessIndex(i)) + setIndex(i, byteValue); + } + + private: + JSByteArray(ExecState*, Structure*, ByteArray* storage); + + public: + static JSByteArray* create(ExecState* exec, Structure* structure, ByteArray* storage) + { + JSByteArray* array = new (NotNull, allocateCell<JSByteArray>(*exec->heap())) JSByteArray(exec, structure, storage); + array->finishCreation(exec); + return array; + } + + static Structure* createStructure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const JSC::ClassInfo* = &s_info); + + static bool getOwnPropertySlot(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); + static bool getOwnPropertySlotByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + static void put(JSC::JSCell*, JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&); + static void putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue); + + static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, EnumerationMode); + + static JS_EXPORTDATA const ClassInfo s_info; + + size_t length() const { return m_storage->length(); } + + WTF::ByteArray* storage() const { return m_storage.get(); } + + ~JSByteArray(); + static void destroy(JSCell*); + + static size_t offsetOfStorage() { return OBJECT_OFFSETOF(JSByteArray, m_storage); } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + + void finishCreation(ExecState* exec) + { + Base::finishCreation(exec->globalData()); + putDirect(exec->globalData(), exec->globalData().propertyNames->length, jsNumber(m_storage->length()), ReadOnly | DontDelete); + } + + private: + RefPtr<WTF::ByteArray> m_storage; + }; + + JSByteArray* asByteArray(JSValue value); + inline JSByteArray* asByteArray(JSValue value) + { + return static_cast<JSByteArray*>(value.asCell()); + } + + inline bool isJSByteArray(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSByteArray::s_info; } + +} // namespace JSC + +#endif // JSByteArray_h diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp new file mode 100644 index 000000000..065fd13ea --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCell.cpp @@ -0,0 +1,223 @@ +/* + * 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 "NumberObject.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSCell); + +void JSCell::destroy(JSCell* cell) +{ + cell->JSCell::~JSCell(); +} + +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(JSCell*, CallData&) +{ + return CallTypeNone; +} + +ConstructType JSCell::getConstructData(JSCell*, ConstructData&) +{ + return ConstructTypeNone; +} + +bool JSCell::getOwnPropertySlot(JSCell* cell, 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 = cell->toObject(exec, exec->lexicalGlobalObject()); + slot.setBase(object); + if (!object->getPropertySlot(exec, identifier, slot)) + slot.setUndefined(); + return true; +} + +bool JSCell::getOwnPropertySlotByIndex(JSCell* cell, 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 = cell->toObject(exec, exec->lexicalGlobalObject()); + slot.setBase(object); + if (!object->getPropertySlot(exec, identifier, slot)) + slot.setUndefined(); + return true; +} + +void JSCell::put(JSCell* cell, ExecState* exec, const Identifier& identifier, JSValue value, PutPropertySlot& slot) +{ + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + thisObject->methodTable()->put(thisObject, exec, identifier, value, slot); +} + +void JSCell::putByIndex(JSCell* cell, ExecState* exec, unsigned identifier, JSValue value) +{ + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + thisObject->methodTable()->putByIndex(thisObject, exec, identifier, value); +} + +bool JSCell::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& identifier) +{ + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + return thisObject->methodTable()->deleteProperty(thisObject, exec, identifier); +} + +bool JSCell::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned identifier) +{ + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, identifier); +} + +JSObject* JSCell::toThisObject(JSCell* cell, ExecState* exec) +{ + return cell->toObject(exec, exec->lexicalGlobalObject()); +} + +JSValue JSCell::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toPrimitive(exec, preferredType); + return static_cast<const JSObject*>(this)->toPrimitive(exec, preferredType); +} + +bool JSCell::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value) const +{ + if (isString()) + return static_cast<const JSString*>(this)->getPrimitiveNumber(exec, number, value); + return static_cast<const JSObject*>(this)->getPrimitiveNumber(exec, number, value); +} + +double JSCell::toNumber(ExecState* exec) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toNumber(exec); + return static_cast<const JSObject*>(this)->toNumber(exec); +} + +UString JSCell::toString(ExecState* exec) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toString(exec); + return static_cast<const JSObject*>(this)->toString(exec); +} + +JSObject* JSCell::toObject(ExecState* exec, JSGlobalObject* globalObject) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toObject(exec, globalObject); + ASSERT(isObject()); + return static_cast<JSObject*>(const_cast<JSCell*>(this)); +} + +void slowValidateCell(JSCell* cell) +{ + ASSERT_GC_OBJECT_LOOKS_VALID(cell); +} + +void JSCell::defineGetter(JSObject*, ExecState*, const Identifier&, JSObject*, unsigned) +{ + ASSERT_NOT_REACHED(); +} + +void JSCell::defineSetter(JSObject*, ExecState*, const Identifier&, JSObject*, unsigned) +{ + ASSERT_NOT_REACHED(); +} + +JSValue JSCell::defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType) +{ + ASSERT_NOT_REACHED(); + return jsUndefined(); +} + +void JSCell::getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + ASSERT_NOT_REACHED(); +} + +UString JSCell::className(const JSObject*) +{ + ASSERT_NOT_REACHED(); + return UString(); +} + +void JSCell::getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + ASSERT_NOT_REACHED(); +} + +bool JSCell::hasInstance(JSObject*, ExecState*, JSValue, JSValue) +{ + ASSERT_NOT_REACHED(); + return false; +} + +void JSCell::putWithAttributes(JSObject*, ExecState*, const Identifier&, JSValue, unsigned) +{ + ASSERT_NOT_REACHED(); +} + +bool JSCell::defineOwnProperty(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&, bool) +{ + ASSERT_NOT_REACHED(); + return false; +} + +bool JSCell::getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&) +{ + ASSERT_NOT_REACHED(); + return false; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h new file mode 100644 index 000000000..47e336a86 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -0,0 +1,344 @@ +/* + * 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 "CallFrame.h" +#include "ConstructData.h" +#include "Heap.h" +#include "JSLock.h" +#include "JSValueInlineMethods.h" +#include "SlotVisitor.h" +#include "WriteBarrier.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + + class JSGlobalObject; + class Structure; + class PropertyDescriptor; + class PropertyNameArray; + + enum EnumerationMode { + ExcludeDontEnumProperties, + IncludeDontEnumProperties + }; + + enum TypedArrayType { + TypedArrayNone, + TypedArrayInt8, + TypedArrayInt16, + TypedArrayInt32, + TypedArrayUint8, + TypedArrayUint16, + TypedArrayUint32, + TypedArrayFloat32, + TypedArrayFloat64 + }; + + class JSCell { + friend class JSValue; + friend class MarkedBlock; + + public: + enum CreatingEarlyCellTag { CreatingEarlyCell }; + JSCell(CreatingEarlyCellTag); + + protected: + JSCell(JSGlobalData&, Structure*); + static void destroy(JSCell*); + + public: + // Querying the type. + bool isString() const; + bool isObject() const; + bool isGetterSetter() const; + bool inherits(const ClassInfo*) const; + bool isAPIValueWrapper() const; + + Structure* structure() const; + void setStructure(JSGlobalData&, Structure*); + void clearStructure() { m_structure.clear(); } + + // 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 + + static CallType getCallData(JSCell*, CallData&); + static ConstructType getConstructData(JSCell*, ConstructData&); + + // Basic conversions. + JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + bool toBoolean(ExecState*) const; + double toNumber(ExecState*) const; + UString toString(ExecState*) const; + JSObject* toObject(ExecState*, JSGlobalObject*) const; + + static void visitChildren(JSCell*, SlotVisitor&); + + // Object operations, with the toObject operation included. + const ClassInfo* classInfo() const; + const ClassInfo* validatedClassInfo() const; + const MethodTable* methodTable() const; + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + static JSObject* toThisObject(JSCell*, ExecState*); + + void zap() { *reinterpret_cast<uintptr_t**>(this) = 0; } + bool isZapped() const { return !*reinterpret_cast<uintptr_t* const*>(this); } + + // 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&); + JSValue fastGetOwnProperty(ExecState*, const UString&); + + static ptrdiff_t structureOffset() + { + return OBJECT_OFFSETOF(JSCell, m_structure); + } + + static ptrdiff_t classInfoOffset() + { + return OBJECT_OFFSETOF(JSCell, m_classInfo); + } + + void* structureAddress() + { + return &m_structure; + } + +#if ENABLE(GC_VALIDATION) + Structure* unvalidatedStructure() { return m_structure.unvalidatedGet(); } +#endif + + static const TypedArrayType TypedArrayStorageType = TypedArrayNone; + protected: + + void finishCreation(JSGlobalData&); + void finishCreation(JSGlobalData&, Structure*, CreatingEarlyCellTag); + + // Base implementation; for non-object classes implements getPropertySlot. + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + + // Dummy implementations of override-able static functions for classes to put in their MethodTable + static NO_RETURN_DUE_TO_ASSERT void defineGetter(JSObject*, ExecState*, const Identifier&, JSObject*, unsigned); + static NO_RETURN_DUE_TO_ASSERT void defineSetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + static NO_RETURN_DUE_TO_ASSERT void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static NO_RETURN_DUE_TO_ASSERT void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static UString className(const JSObject*); + static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty); + static NO_RETURN_DUE_TO_ASSERT void putWithAttributes(JSObject*, ExecState*, const Identifier& propertyName, JSValue, unsigned attributes); + static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + private: + const ClassInfo* m_classInfo; + WriteBarrier<Structure> m_structure; + }; + + inline JSCell::JSCell(CreatingEarlyCellTag) + { + } + + inline void JSCell::finishCreation(JSGlobalData& globalData) + { +#if ENABLE(GC_VALIDATION) + ASSERT(globalData.isInitializingObject()); + globalData.setInitializingObject(false); +#else + UNUSED_PARAM(globalData); +#endif + ASSERT(m_structure); + } + + inline Structure* JSCell::structure() const + { + return m_structure.get(); + } + + inline const ClassInfo* JSCell::classInfo() const + { + return m_classInfo; + } + + inline void JSCell::visitChildren(JSCell* cell, SlotVisitor& visitor) + { + visitor.append(&cell->m_structure); + } + + // --- JSValue inlines ---------------------------- + + inline bool JSValue::isString() const + { + return isCell() && asCell()->isString(); + } + + inline bool JSValue::isPrimitive() 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(); + } + + template <typename Base> UString HandleConverter<Base, Unknown>::getString(ExecState* exec) const + { + return jsValue().getString(exec); + } + + inline JSObject* JSValue::getObject() const + { + return isCell() ? asCell()->getObject() : 0; + } + + 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; + } + + 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 = std::numeric_limits<double>::quiet_NaN(); + value = *this; + return true; + } + + ALWAYS_INLINE double JSValue::toNumber(ExecState* exec) const + { + if (isInt32()) + return asInt32(); + if (isDouble()) + return asDouble(); + return toNumberSlowCase(exec); + } + + inline JSObject* JSValue::toObject(ExecState* exec) const + { + return isCell() ? asCell()->toObject(exec, exec->lexicalGlobalObject()) : toObjectSlowCase(exec, exec->lexicalGlobalObject()); + } + + inline JSObject* JSValue::toObject(ExecState* exec, JSGlobalObject* globalObject) const + { + return isCell() ? asCell()->toObject(exec, globalObject) : toObjectSlowCase(exec, globalObject); + } + + template <typename T> void* allocateCell(Heap& heap) + { +#if ENABLE(GC_VALIDATION) + ASSERT(sizeof(T) == T::s_info.cellSize); + ASSERT(!heap.globalData()->isInitializingObject()); + heap.globalData()->setInitializingObject(true); +#endif + JSCell* result = static_cast<JSCell*>(heap.allocate(sizeof(T))); + result->clearStructure(); + return result; + } + + inline bool isZapped(const JSCell* cell) + { + return cell->isZapped(); + } + + template<typename To, typename From> + inline To jsCast(From* from) + { + ASSERT(from->inherits(&WTF::RemovePointer<To>::Type::s_info)); + return static_cast<To>(from); + } + + template<typename To, typename From> + inline To jsDynamicCast(From* from) + { + return from->inherits(&WTF::RemovePointer<To>::Type::s_info) ? static_cast<To>(from) : 0; + } + +} // namespace JSC + +#endif // JSCell_h diff --git a/Source/JavaScriptCore/runtime/JSChunk.cpp b/Source/JavaScriptCore/runtime/JSChunk.cpp new file mode 100644 index 000000000..f064de8bb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSChunk.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 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 "JSChunk.h" + diff --git a/Source/JavaScriptCore/runtime/JSChunk.h b/Source/JavaScriptCore/runtime/JSChunk.h new file mode 100644 index 000000000..bae2bc75f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSChunk.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 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 JSChunk_h +#define JSChunk_h + + + +#endif // JSChunk_h diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp new file mode 100644 index 000000000..863073338 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp @@ -0,0 +1,267 @@ +/* + * 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. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010 &yet, LLC. (nate@andyet.net) + * + * 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. + + * Copyright 2006-2008 the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "JSDateMath.h" + +#include "Assertions.h" +#include "ASCIICType.h" +#include "CurrentTime.h" +#include "JSObject.h" +#include "MathExtras.h" +#include "ScopeChain.h" +#include "StdLibExtras.h" +#include "StringExtras.h" + +#include <algorithm> +#include <limits.h> +#include <limits> +#include <stdint.h> +#include <time.h> +#include <wtf/text/StringBuilder.h> + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif + +#if OS(WINCE) +extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); +extern "C" struct tm * localtime(const time_t *timer); +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +using namespace WTF; + +namespace JSC { + +static inline double timeToMS(double hour, double min, double sec, double ms) +{ + return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms); +} + +static inline int msToSeconds(double ms) +{ + double result = fmod(floor(ms / msPerSecond), secondsPerMinute); + if (result < 0) + result += secondsPerMinute; + return static_cast<int>(result); +} + +// 0: Sunday, 1: Monday, etc. +static inline int msToWeekDay(double ms) +{ + int wd = (static_cast<int>(msToDays(ms)) + 4) % 7; + if (wd < 0) + wd += 7; + return wd; +} + +// Get the DST offset for the time passed in. +// +// NOTE: The implementation relies on the fact that no time zones have +// more than one daylight savings offset change per month. +// If this function is called with NaN it returns NaN. +static double getDSTOffset(ExecState* exec, double ms, double utcOffset) +{ + DSTOffsetCache& cache = exec->globalData().dstOffsetCache; + double start = cache.start; + double end = cache.end; + + if (start <= ms) { + // If the time fits in the cached interval, return the cached offset. + if (ms <= end) return cache.offset; + + // Compute a possible new interval end. + double newEnd = end + cache.increment; + + if (ms <= newEnd) { + double endOffset = calculateDSTOffset(newEnd, utcOffset); + if (cache.offset == endOffset) { + // If the offset at the end of the new interval still matches + // the offset in the cache, we grow the cached time interval + // and return the offset. + cache.end = newEnd; + cache.increment = msPerMonth; + return endOffset; + } else { + double offset = calculateDSTOffset(ms, utcOffset); + if (offset == endOffset) { + // The offset at the given time is equal to the offset at the + // new end of the interval, so that means that we've just skipped + // the point in time where the DST offset change occurred. Updated + // the interval to reflect this and reset the increment. + cache.start = ms; + cache.end = newEnd; + cache.increment = msPerMonth; + } else { + // The interval contains a DST offset change and the given time is + // before it. Adjust the increment to avoid a linear search for + // the offset change point and change the end of the interval. + cache.increment /= 3; + cache.end = ms; + } + // Update the offset in the cache and return it. + cache.offset = offset; + return offset; + } + } + } + + // Compute the DST offset for the time and shrink the cache interval + // to only contain the time. This allows fast repeated DST offset + // computations for the same time. + double offset = calculateDSTOffset(ms, utcOffset); + cache.offset = offset; + cache.start = ms; + cache.end = ms; + cache.increment = msPerMonth; + return offset; +} + +/* + * Get the difference in milliseconds between this time zone and UTC (GMT) + * NOT including DST. + */ +double getUTCOffset(ExecState* exec) +{ + double utcOffset = exec->globalData().cachedUTCOffset; + if (!isnan(utcOffset)) + return utcOffset; + exec->globalData().cachedUTCOffset = calculateUTCOffset(); + return exec->globalData().cachedUTCOffset; +} + +double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) +{ + double day = dateToDaysFrom1970(t.year + 1900, t.month, t.monthDay); + double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); + double result = (day * WTF::msPerDay) + ms; + + if (!inputIsUTC) { // convert to UTC + double utcOffset = getUTCOffset(exec); + result -= utcOffset; + result -= getDSTOffset(exec, result, utcOffset); + } + + return result; +} + +// input is UTC +void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm) +{ + double dstOff = 0.0; + double utcOff = 0.0; + if (!outputIsUTC) { + utcOff = getUTCOffset(exec); + dstOff = getDSTOffset(exec, ms, utcOff); + ms += dstOff + utcOff; + } + + const int year = msToYear(ms); + tm.second = msToSeconds(ms); + tm.minute = msToMinutes(ms); + tm.hour = msToHours(ms); + tm.weekDay = msToWeekDay(ms); + tm.yearDay = dayInYear(ms, year); + tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); + tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); + tm.year = year - 1900; + tm.isDST = dstOff != 0.0; + tm.utcOffset = static_cast<long>((dstOff + utcOff) / WTF::msPerSecond); + tm.timeZone = nullptr; +} + +double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString) +{ + ASSERT(exec); + bool haveTZ; + int offset; + double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (isnan(ms)) + return std::numeric_limits<double>::quiet_NaN(); + + // fall back to local timezone + if (!haveTZ) { + double utcOffset = getUTCOffset(exec); + double dstOffset = getDSTOffset(exec, ms, utcOffset); + offset = static_cast<int>((utcOffset + dstOffset) / WTF::msPerMinute); + } + return ms - (offset * WTF::msPerMinute); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSDateMath.h b/Source/JavaScriptCore/runtime/JSDateMath.h new file mode 100644 index 000000000..ba6d647dd --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDateMath.h @@ -0,0 +1,169 @@ +/* + * 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. + * Copyright (C) 2010 Research In Motion Limited. 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 JSDateMath_h +#define JSDateMath_h + +#include <wtf/DateMath.h> + +namespace JSC { + +class ExecState; +struct GregorianDateTime; + +void msToGregorianDateTime(ExecState*, double, bool outputIsUTC, GregorianDateTime&); +double gregorianDateTimeToMS(ExecState*, const GregorianDateTime&, double, bool inputIsUTC); +double getUTCOffset(ExecState*); +double parseDateFromNullTerminatedCharacters(ExecState*, const char* dateString); + +// Intentionally overridding the default tm of the system. +// The members of tm differ on various operating systems. +struct GregorianDateTime { + WTF_MAKE_NONCOPYABLE(GregorianDateTime); +public: + GregorianDateTime() + : second(0) + , minute(0) + , hour(0) + , weekDay(0) + , monthDay(0) + , yearDay(0) + , month(0) + , year(0) + , isDST(0) + , utcOffset(0) + { + } + + GregorianDateTime(ExecState* exec, const tm& inTm) + : second(inTm.tm_sec) + , minute(inTm.tm_min) + , hour(inTm.tm_hour) + , weekDay(inTm.tm_wday) + , monthDay(inTm.tm_mday) + , yearDay(inTm.tm_yday) + , month(inTm.tm_mon) + , year(inTm.tm_year) + , isDST(inTm.tm_isdst) + { + UNUSED_PARAM(exec); +#if HAVE(TM_GMTOFF) + utcOffset = static_cast<int>(inTm.tm_gmtoff); +#else + utcOffset = static_cast<int>(getUTCOffset(exec) / WTF::msPerSecond + (isDST ? WTF::secondsPerHour : 0)); +#endif + +#if HAVE(TM_ZONE) + int inZoneSize = strlen(inTm.tm_zone) + 1; + timeZone = adoptArrayPtr(new char[inZoneSize]); + strncpy(timeZone.get(), inTm.tm_zone, inZoneSize); +#else + timeZone = nullptr; +#endif + } + + operator tm() const + { + tm ret; + memset(&ret, 0, sizeof(ret)); + + ret.tm_sec = second; + ret.tm_min = minute; + ret.tm_hour = hour; + ret.tm_wday = weekDay; + ret.tm_mday = monthDay; + ret.tm_yday = yearDay; + ret.tm_mon = month; + ret.tm_year = year; + ret.tm_isdst = isDST; + +#if HAVE(TM_GMTOFF) + ret.tm_gmtoff = static_cast<long>(utcOffset); +#endif +#if HAVE(TM_ZONE) + ret.tm_zone = timeZone.get(); +#endif + + return ret; + } + + void copyFrom(const GregorianDateTime& rhs) + { + second = rhs.second; + minute = rhs.minute; + hour = rhs.hour; + weekDay = rhs.weekDay; + monthDay = rhs.monthDay; + yearDay = rhs.yearDay; + month = rhs.month; + year = rhs.year; + isDST = rhs.isDST; + utcOffset = rhs.utcOffset; + if (rhs.timeZone) { + int inZoneSize = strlen(rhs.timeZone.get()) + 1; + timeZone = adoptArrayPtr(new char[inZoneSize]); + strncpy(timeZone.get(), rhs.timeZone.get(), inZoneSize); + } else + timeZone = nullptr; + } + + int second; + int minute; + int hour; + int weekDay; + int monthDay; + int yearDay; + int month; + int year; + int isDST; + int utcOffset; + OwnArrayPtr<char> timeZone; +}; + +static inline int gmtoffset(const GregorianDateTime& t) +{ + return t.utcOffset; +} + +} // namespace JSC + +#endif // JSDateMath_h diff --git a/Source/JavaScriptCore/runtime/JSExportMacros.h b/Source/JavaScriptCore/runtime/JSExportMacros.h new file mode 100644 index 000000000..884805f86 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSExportMacros.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 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. + * + * This file handles shared library symbol export decorations. It is recommended + * that all WebKit projects use these definitions so that symbol exports work + * properly on all platforms and compilers that WebKit builds under. + */ + +#ifndef JSExportMacros_h +#define JSExportMacros_h + +#include <wtf/Platform.h> +#include <wtf/ExportMacros.h> + +// See note in wtf/Platform.h for more info on EXPORT_MACROS. +#if USE(EXPORT_MACROS) + +#if defined(BUILDING_JavaScriptCore) +#define JS_EXPORT_PRIVATE WTF_EXPORT +#else +#define JS_EXPORT_PRIVATE WTF_IMPORT +#endif + +#define JS_EXPORT_HIDDEN WTF_HIDDEN +#define JS_EXPORTDATA JS_EXPORT_PRIVATE +#define JS_EXPORTCLASS JS_EXPORT_PRIVATE + +#else // !USE(EXPORT_MACROS) + +#if !PLATFORM(CHROMIUM) && OS(WINDOWS) && !defined(BUILDING_WX__) && !COMPILER(GCC) + +#if defined(BUILDING_JavaScriptCore) +#define JS_EXPORTDATA __declspec(dllexport) +#else +#define JS_EXPORTDATA __declspec(dllimport) +#endif + +#define JS_EXPORTCLASS JS_EXPORTDATA + +#else // !PLATFORM... + +#define JS_EXPORTDATA +#define JS_EXPORTCLASS + +#endif // !PLATFORM... + +#define JS_EXPORT_PRIVATE +#define JS_EXPORT_HIDDEN + +#endif // USE(EXPORT_MACROS) + +#endif // JSExportMacros_h diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp new file mode 100644 index 000000000..65470a53f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -0,0 +1,362 @@ +/* + * 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 "JSArray.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 { +EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) +{ + return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); +} + +ASSERT_CLASS_FITS_IN_CELL(JSFunction); + +const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFunction) }; + +bool JSFunction::isHostFunctionNonInline() const +{ + return isHostFunction(); +} + +JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction nativeFunction, NativeFunction nativeConstructor) +{ + NativeExecutable* executable = exec->globalData().getHostFunction(nativeFunction, nativeConstructor); + JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure()); + // Can't do this during initialization because getHostFunction might do a GC allocation. + function->finishCreation(exec, executable, length, name); + return function; +} + +JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeExecutable* nativeExecutable) +{ + JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure()); + function->finishCreation(exec, nativeExecutable, length, name); + return function; +} + +JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + : Base(exec->globalData(), structure) + , m_executable() + , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain()) +{ +} + +JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode) + : Base(exec->globalData(), scopeChainNode->globalObject->functionStructure()) + , m_executable(exec->globalData(), this, executable) + , m_scopeChain(exec->globalData(), this, scopeChainNode) +{ +} + +void JSFunction::finishCreation(ExecState* exec, NativeExecutable* executable, int length, const Identifier& name) +{ + Base::finishCreation(exec->globalData()); + ASSERT(inherits(&s_info)); + m_executable.set(exec->globalData(), this, executable); + putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); + putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); +} + +void JSFunction::finishCreation(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode) +{ + Base::finishCreation(exec->globalData()); + ASSERT(inherits(&s_info)); + + // Switching the structure here is only safe if we currently have the function structure! + ASSERT(structure() == scopeChainNode->globalObject->functionStructure()); + setStructure(exec->globalData(), scopeChainNode->globalObject->namedFunctionStructure()); + putDirectOffset(exec->globalData(), scopeChainNode->globalObject->functionNameOffset(), executable->nameValue()); +} + +void JSFunction::destroy(JSCell* cell) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + ASSERT(thisObject->classInfo()->isSubClassOf(&JSFunction::s_info)); + thisObject->JSFunction::~JSFunction(); +} + +const UString& JSFunction::name(ExecState* exec) +{ + return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue(); +} + +const UString JSFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName); + + if (displayName && isJSString(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); +} + +const SourceCode* JSFunction::sourceCode() const +{ + if (isHostFunction()) + return 0; + return &jsExecutable()->source(); +} + +void JSFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_scopeChain); + if (thisObject->m_executable) + visitor.append(&thisObject->m_executable); +} + +CallType JSFunction::getCallData(JSCell* cell, CallData& callData) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostFunction()) { + callData.native.function = thisObject->nativeFunction(); + return CallTypeHost; + } + callData.js.functionExecutable = thisObject->jsExecutable(); + callData.js.scopeChain = thisObject->scope(); + 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(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostFunction()) + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + + if (propertyName == exec->propertyNames().prototype) { + WriteBarrierBase<Unknown>* location = thisObject->getDirectLocation(exec->globalData(), propertyName); + + if (!location) { + JSObject* prototype = constructEmptyObject(exec, thisObject->globalObject()->emptyObjectStructure()); + prototype->putDirect(exec->globalData(), exec->propertyNames().constructor, thisObject, DontEnum); + PutPropertySlot slot; + thisObject->putDirect(exec->globalData(), exec->propertyNames().prototype, prototype, DontDelete | DontEnum, false, slot); + location = thisObject->getDirectLocation(exec->globalData(), exec->propertyNames().prototype); + } + + slot.setValue(thisObject, location->get(), thisObject->offsetForLocation(location)); + } + + if (propertyName == exec->propertyNames().arguments) { + if (thisObject->jsExecutable()->isStrictMode()) { + bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + if (!result) { + thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); + result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + ASSERT(result); + } + return result; + } + slot.setCacheableCustom(thisObject, argumentsGetter); + return true; + } + + if (propertyName == exec->propertyNames().length) { + slot.setCacheableCustom(thisObject, lengthGetter); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + if (thisObject->jsExecutable()->isStrictMode()) { + bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + if (!result) { + thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); + result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + ASSERT(result); + } + return result; + } + slot.setCacheableCustom(thisObject, callerGetter); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +bool JSFunction::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSFunction* thisObject = jsCast<JSFunction*>(object); + if (thisObject->isHostFunction()) + return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + + if (propertyName == exec->propertyNames().prototype) { + PropertySlot slot; + thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); + return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + } + + if (propertyName == exec->propertyNames().arguments) { + if (thisObject->jsExecutable()->isStrictMode()) { + bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + if (!result) { + thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); + result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + ASSERT(result); + } + return result; + } + descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, thisObject), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(thisObject->jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + if (thisObject->jsExecutable()->isStrictMode()) { + bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + if (!result) { + thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); + result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + ASSERT(result); + } + return result; + } + descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, thisObject), ReadOnly | DontEnum | DontDelete); + return true; + } + + return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); +} + +void JSFunction::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSFunction* thisObject = jsCast<JSFunction*>(object); + if (!thisObject->isHostFunction() && (mode == IncludeDontEnumProperties)) { + // Make sure prototype has been reified. + PropertySlot slot; + thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, exec->propertyNames().prototype, slot); + + propertyNames.add(exec->propertyNames().arguments); + propertyNames.add(exec->propertyNames().caller); + propertyNames.add(exec->propertyNames().length); + } + Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode); +} + +void JSFunction::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostFunction()) { + Base::put(thisObject, 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; + thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); + } + if (thisObject->jsExecutable()->isStrictMode() && (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().caller)) { + // This will trigger the property to be reified, if this is not already the case! + bool okay = thisObject->hasProperty(exec, propertyName); + ASSERT_UNUSED(okay, okay); + Base::put(thisObject, exec, propertyName, value, slot); + return; + } + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + return; + Base::put(thisObject, exec, propertyName, value, slot); +} + +bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostFunction()) + return Base::deleteProperty(thisObject, exec, propertyName); + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + return false; + return Base::deleteProperty(thisObject, exec, propertyName); +} + +// ECMA 13.2.2 [[Construct]] +ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostFunction()) { + constructData.native.function = thisObject->nativeConstructor(); + return ConstructTypeHost; + } + constructData.js.functionExecutable = thisObject->jsExecutable(); + constructData.js.scopeChain = thisObject->scope(); + return ConstructTypeJS; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h new file mode 100644 index 000000000..5118f8b10 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -0,0 +1,163 @@ +/* + * 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 "JSObject.h" + +namespace JSC { + + class ExecutableBase; + class FunctionExecutable; + class FunctionPrototype; + class JSActivation; + class JSGlobalObject; + class NativeExecutable; + class SourceCode; + namespace DFG { + class SpeculativeJIT; + class JITCompiler; + } + + EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState*); + + class JSFunction : public JSNonFinalObject { + friend class JIT; + friend class DFG::SpeculativeJIT; + friend class DFG::JITCompiler; + friend class JSGlobalData; + + public: + typedef JSNonFinalObject Base; + + static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeFunction nativeFunction, NativeFunction nativeConstructor = callHostFunctionAsConstructor); + static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeExecutable* nativeExecutable); + + static JSFunction* create(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChain) + { + JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, executable, scopeChain); + ASSERT(function->structure()->globalObject()); + function->finishCreation(exec, executable, scopeChain); + return function; + } + + static void destroy(JSCell*); + + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); + + ScopeChainNode* scope() + { + ASSERT(!isHostFunctionNonInline()); + return m_scopeChain.get(); + } + // This method may be called for host functins, in which case it + // will return an arbitrary value. This should only be used for + // optimized paths in which the return value does not matter for + // host functions, and checking whether the function is a host + // function is deemed too expensive. + ScopeChainNode* scopeUnchecked() + { + return m_scopeChain.get(); + } + void setScope(JSGlobalData& globalData, ScopeChainNode* scopeChain) + { + ASSERT(!isHostFunctionNonInline()); + m_scopeChain.set(globalData, this, scopeChain); + } + + ExecutableBase* executable() const { return m_executable.get(); } + + // To call either of these methods include Executable.h + inline bool isHostFunction() const; + FunctionExecutable* jsExecutable() const; + + const SourceCode* sourceCode() const; + + static JS_EXPORTDATA const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(globalData, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), &s_info); + } + + NativeFunction nativeFunction(); + NativeFunction nativeConstructor(); + + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static inline size_t offsetOfScopeChain() + { + return OBJECT_OFFSETOF(JSFunction, m_scopeChain); + } + + static inline size_t offsetOfExecutable() + { + return OBJECT_OFFSETOF(JSFunction, m_executable); + } + + protected: + const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + JSFunction(ExecState*, JSGlobalObject*, Structure*); + JSFunction(ExecState*, FunctionExecutable*, ScopeChainNode*); + + void finishCreation(ExecState*, NativeExecutable*, int length, const Identifier& name); + void finishCreation(ExecState*, FunctionExecutable*, ScopeChainNode*); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties); + + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + + static void visitChildren(JSCell*, SlotVisitor&); + + private: + bool isHostFunctionNonInline() const; + + static JSValue argumentsGetter(ExecState*, JSValue, const Identifier&); + static JSValue callerGetter(ExecState*, JSValue, const Identifier&); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + + WriteBarrier<ExecutableBase> m_executable; + WriteBarrier<ScopeChainNode> m_scopeChain; + }; + + JSFunction* asFunction(JSValue); + + inline JSFunction* asFunction(JSValue value) + { + ASSERT(asObject(value)->inherits(&JSFunction::s_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 000000000..dab3f24ba --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2008, 2011 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 "Heap.h" +#include "CommonIdentifiers.h" +#include "DebuggerActivation.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 "ParserArena.h" +#include "RegExpCache.h" +#include "RegExpObject.h" +#include "StrictEvalActivation.h" +#include "StrongInlines.h" +#include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> + +#if ENABLE(REGEXP_TRACING) +#include "RegExp.h" +#endif + +#if PLATFORM(MAC) +#include <CoreFoundation/CoreFoundation.h> +#endif + +using namespace WTF; + +namespace { + +using namespace JSC; + +class Recompiler : public MarkedBlock::VoidFunctor { +public: + void operator()(JSCell*); +}; + +inline void Recompiler::operator()(JSCell* cell) +{ + if (!cell->inherits(&JSFunction::s_info)) + return; + JSFunction* function = asFunction(cell); + if (!function->executable() || function->executable()->isHostFunction()) + return; + function->jsExecutable()->discardCode(); +} + +} // namespace + +namespace JSC { + +extern const HashTable arrayConstructorTable; +extern const HashTable arrayPrototypeTable; +extern const HashTable booleanPrototypeTable; +extern const HashTable jsonTable; +extern const HashTable dateTable; +extern const HashTable dateConstructorTable; +extern const HashTable errorPrototypeTable; +extern const HashTable globalObjectTable; +extern const HashTable mathTable; +extern const HashTable numberConstructorTable; +extern const HashTable numberPrototypeTable; +extern const HashTable objectConstructorTable; +extern const HashTable objectPrototypeTable; +extern const HashTable regExpTable; +extern const HashTable regExpConstructorTable; +extern const HashTable regExpPrototypeTable; +extern const HashTable stringTable; +extern const HashTable stringConstructorTable; + +JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType, HeapSize heapSize) + : globalDataType(globalDataType) + , clientData(0) + , topCallFrame(CallFrame::noCaller()) + , arrayConstructorTable(fastNew<HashTable>(JSC::arrayConstructorTable)) + , arrayPrototypeTable(fastNew<HashTable>(JSC::arrayPrototypeTable)) + , booleanPrototypeTable(fastNew<HashTable>(JSC::booleanPrototypeTable)) + , dateTable(fastNew<HashTable>(JSC::dateTable)) + , dateConstructorTable(fastNew<HashTable>(JSC::dateConstructorTable)) + , errorPrototypeTable(fastNew<HashTable>(JSC::errorPrototypeTable)) + , globalObjectTable(fastNew<HashTable>(JSC::globalObjectTable)) + , jsonTable(fastNew<HashTable>(JSC::jsonTable)) + , mathTable(fastNew<HashTable>(JSC::mathTable)) + , numberConstructorTable(fastNew<HashTable>(JSC::numberConstructorTable)) + , numberPrototypeTable(fastNew<HashTable>(JSC::numberPrototypeTable)) + , objectConstructorTable(fastNew<HashTable>(JSC::objectConstructorTable)) + , objectPrototypeTable(fastNew<HashTable>(JSC::objectPrototypeTable)) + , regExpTable(fastNew<HashTable>(JSC::regExpTable)) + , regExpConstructorTable(fastNew<HashTable>(JSC::regExpConstructorTable)) + , regExpPrototypeTable(fastNew<HashTable>(JSC::regExpPrototypeTable)) + , stringTable(fastNew<HashTable>(JSC::stringTable)) + , stringConstructorTable(fastNew<HashTable>(JSC::stringConstructorTable)) + , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) + , propertyNames(new CommonIdentifiers(this)) + , emptyList(new MarkedArgumentBuffer) +#if ENABLE(ASSEMBLER) + , executableAllocator(*this) +#endif + , parserArena(adoptPtr(new ParserArena)) + , keywords(adoptPtr(new Keywords(this))) + , interpreter(0) + , heap(this, heapSize) +#if ENABLE(DFG_JIT) + , sizeOfLastScratchBuffer(0) +#endif + , dynamicGlobalObject(0) + , cachedUTCOffset(std::numeric_limits<double>::quiet_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 CPU(X86) && ENABLE(JIT) + , m_timeoutCount(512) +#endif +#if ENABLE(GC_VALIDATION) + , m_isInitializingObject(false) +#endif +{ + interpreter = new Interpreter; + if (globalDataType == Default) + m_stack = wtfThreadData().stack(); + + // Need to be careful to keep everything consistent here + IdentifierTable* existingEntryIdentifierTable = wtfThreadData().setCurrentIdentifierTable(identifierTable); + JSLock lock(SilenceAssertionsOnly); + structureStructure.set(*this, Structure::createStructure(*this)); + debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, 0, jsNull())); + activationStructure.set(*this, JSActivation::createStructure(*this, 0, jsNull())); + interruptedExecutionErrorStructure.set(*this, InterruptedExecutionError::createStructure(*this, 0, jsNull())); + terminatedExecutionErrorStructure.set(*this, TerminatedExecutionError::createStructure(*this, 0, jsNull())); + staticScopeStructure.set(*this, JSStaticScopeObject::createStructure(*this, 0, jsNull())); + strictEvalActivationStructure.set(*this, StrictEvalActivation::createStructure(*this, 0, jsNull())); + stringStructure.set(*this, JSString::createStructure(*this, 0, jsNull())); + notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, 0, jsNull())); + propertyNameIteratorStructure.set(*this, JSPropertyNameIterator::createStructure(*this, 0, jsNull())); + getterSetterStructure.set(*this, GetterSetter::createStructure(*this, 0, jsNull())); + apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, 0, jsNull())); + scopeChainNodeStructure.set(*this, ScopeChainNode::createStructure(*this, 0, jsNull())); + executableStructure.set(*this, ExecutableBase::createStructure(*this, 0, jsNull())); + nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, 0, jsNull())); + evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, 0, jsNull())); + programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, 0, jsNull())); + functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, 0, jsNull())); + regExpStructure.set(*this, RegExp::createStructure(*this, 0, jsNull())); + structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull())); + + wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); + +#if ENABLE(JIT) && ENABLE(INTERPRETER) +#if USE(CF) + CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); + CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); + if (canUseJIT) { + m_canUseJIT = kCFBooleanTrue == canUseJIT; + CFRelease(canUseJIT); + } else { + char* canUseJITString = getenv("JavaScriptCoreUseJIT"); + m_canUseJIT = !canUseJITString || atoi(canUseJITString); + } + CFRelease(canUseJITKey); +#elif OS(UNIX) + char* canUseJITString = getenv("JavaScriptCoreUseJIT"); + m_canUseJIT = !canUseJITString || atoi(canUseJITString); +#else + m_canUseJIT = true; +#endif +#endif +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (m_canUseJIT) + m_canUseJIT = executableAllocator.isValid(); +#endif + jitStubs = adoptPtr(new JITThunks(this)); +#endif + + interpreter->initialize(this->canUseJIT()); + + heap.notifyIsSafeToCollect(); +} + +void JSGlobalData::clearBuiltinStructures() +{ + structureStructure.clear(); + debuggerActivationStructure.clear(); + activationStructure.clear(); + interruptedExecutionErrorStructure.clear(); + terminatedExecutionErrorStructure.clear(); + staticScopeStructure.clear(); + strictEvalActivationStructure.clear(); + stringStructure.clear(); + notAnObjectStructure.clear(); + propertyNameIteratorStructure.clear(); + getterSetterStructure.clear(); + apiWrapperStructure.clear(); + scopeChainNodeStructure.clear(); + executableStructure.clear(); + nativeExecutableStructure.clear(); + evalExecutableStructure.clear(); + programExecutableStructure.clear(); + functionExecutableStructure.clear(); + regExpStructure.clear(); + structureChainStructure.clear(); +} + +JSGlobalData::~JSGlobalData() +{ + // By the time this is destroyed, heap.destroy() must already have been called. + + delete interpreter; +#ifndef NDEBUG + // Zeroing out to make the behavior more predictable when someone attempts to use a deleted instance. + interpreter = 0; +#endif + + arrayPrototypeTable->deleteTable(); + arrayConstructorTable->deleteTable(); + booleanPrototypeTable->deleteTable(); + dateTable->deleteTable(); + dateConstructorTable->deleteTable(); + errorPrototypeTable->deleteTable(); + globalObjectTable->deleteTable(); + jsonTable->deleteTable(); + mathTable->deleteTable(); + numberConstructorTable->deleteTable(); + numberPrototypeTable->deleteTable(); + objectConstructorTable->deleteTable(); + objectPrototypeTable->deleteTable(); + regExpTable->deleteTable(); + regExpConstructorTable->deleteTable(); + regExpPrototypeTable->deleteTable(); + stringTable->deleteTable(); + stringConstructorTable->deleteTable(); + + fastDelete(const_cast<HashTable*>(arrayConstructorTable)); + fastDelete(const_cast<HashTable*>(arrayPrototypeTable)); + fastDelete(const_cast<HashTable*>(booleanPrototypeTable)); + fastDelete(const_cast<HashTable*>(dateTable)); + fastDelete(const_cast<HashTable*>(dateConstructorTable)); + fastDelete(const_cast<HashTable*>(errorPrototypeTable)); + fastDelete(const_cast<HashTable*>(globalObjectTable)); + fastDelete(const_cast<HashTable*>(jsonTable)); + fastDelete(const_cast<HashTable*>(mathTable)); + fastDelete(const_cast<HashTable*>(numberConstructorTable)); + fastDelete(const_cast<HashTable*>(numberPrototypeTable)); + fastDelete(const_cast<HashTable*>(objectConstructorTable)); + fastDelete(const_cast<HashTable*>(objectPrototypeTable)); + fastDelete(const_cast<HashTable*>(regExpTable)); + fastDelete(const_cast<HashTable*>(regExpConstructorTable)); + fastDelete(const_cast<HashTable*>(regExpPrototypeTable)); + fastDelete(const_cast<HashTable*>(stringTable)); + fastDelete(const_cast<HashTable*>(stringConstructorTable)); + + opaqueJSClassData.clear(); + + delete emptyList; + + delete propertyNames; + if (globalDataType != Default) + deleteIdentifierTable(identifierTable); + + delete clientData; + delete m_regExpCache; +#if ENABLE(REGEXP_TRACING) + delete m_rtTraceList; +#endif + +#if ENABLE(DFG_JIT) + for (unsigned i = 0; i < scratchBuffers.size(); ++i) + fastFree(scratchBuffers[i]); +#endif +} + +PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type, HeapSize heapSize) +{ + return adoptRef(new JSGlobalData(APIContextGroup, type, heapSize)); +} + +PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type, HeapSize heapSize) +{ + return adoptRef(new JSGlobalData(Default, type, heapSize)); +} + +PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type, HeapSize heapSize) +{ + return create(type, heapSize); +} + +bool JSGlobalData::sharedInstanceExists() +{ + return sharedInstanceInternal(); +} + +JSGlobalData& JSGlobalData::sharedInstance() +{ + JSGlobalData*& instance = sharedInstanceInternal(); + if (!instance) { + instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall, SmallHeap)).leakRef(); + instance->makeUsableFromMultipleThreads(); + } + return *instance; +} + +JSGlobalData*& JSGlobalData::sharedInstanceInternal() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + static JSGlobalData* sharedInstance; + return sharedInstance; +} + +#if ENABLE(JIT) +static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) +{ + switch (intrinsic) { + case CharCodeAtIntrinsic: + return charCodeAtThunkGenerator; + case CharAtIntrinsic: + return charAtThunkGenerator; + case FromCharCodeIntrinsic: + return fromCharCodeThunkGenerator; + case SqrtIntrinsic: + return sqrtThunkGenerator; + case PowIntrinsic: + return powThunkGenerator; + case AbsIntrinsic: + return absThunkGenerator; + case FloorIntrinsic: + return floorThunkGenerator; + case CeilIntrinsic: + return ceilThunkGenerator; + case RoundIntrinsic: + return roundThunkGenerator; + case ExpIntrinsic: + return expThunkGenerator; + case LogIntrinsic: + return logThunkGenerator; + default: + return 0; + } +} + +NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, NativeFunction constructor) +{ +#if ENABLE(INTERPRETER) + if (!canUseJIT()) + return NativeExecutable::create(*this, function, constructor); +#endif + return jitStubs->hostFunctionStub(this, function, constructor); +} +NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, Intrinsic intrinsic) +{ + ASSERT(canUseJIT()); + return jitStubs->hostFunctionStub(this, function, intrinsic != NoIntrinsic ? thunkGeneratorForIntrinsic(intrinsic) : 0, intrinsic); +} +#else +NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, NativeFunction constructor) +{ + return NativeExecutable::create(*this, function, constructor); +} +#endif + +JSGlobalData::ClientData::~ClientData() +{ +} + +void JSGlobalData::resetDateCache() +{ + cachedUTCOffset = std::numeric_limits<double>::quiet_NaN(); + dstOffsetCache.reset(); + cachedDateString = UString(); + cachedDateStringValue = std::numeric_limits<double>::quiet_NaN(); + dateInstanceCache.reset(); +} + +void JSGlobalData::startSampling() +{ + interpreter->startSampling(); +} + +void JSGlobalData::stopSampling() +{ + interpreter->stopSampling(); +} + +void JSGlobalData::dumpSampleData(ExecState* exec) +{ + interpreter->dumpSampleData(exec); +#if ENABLE(ASSEMBLER) + ExecutableAllocator::dumpProfile(); +#endif +} + +void JSGlobalData::recompileAllJSFunctions() +{ + // If JavaScript is running, it's not safe to recompile, since we'll end + // up throwing away code that is live on the stack. + ASSERT(!dynamicGlobalObject); + + heap.objectSpace().forEachCell<Recompiler>(); +} + +struct StackPreservingRecompiler : public MarkedBlock::VoidFunctor { + HashSet<FunctionExecutable*> currentlyExecutingFunctions; + void operator()(JSCell* cell) + { + if (!cell->inherits(&FunctionExecutable::s_info)) + return; + FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell); + if (currentlyExecutingFunctions.contains(executable)) + return; + executable->discardCode(); + } +}; + +void JSGlobalData::releaseExecutableMemory() +{ + if (dynamicGlobalObject) { + StackPreservingRecompiler recompiler; + HashSet<JSCell*> roots; + heap.getConservativeRegisterRoots(roots); + HashSet<JSCell*>::iterator end = roots.end(); + for (HashSet<JSCell*>::iterator ptr = roots.begin(); ptr != end; ++ptr) { + ScriptExecutable* executable = 0; + JSCell* cell = *ptr; + if (cell->inherits(&ScriptExecutable::s_info)) + executable = static_cast<ScriptExecutable*>(*ptr); + else if (cell->inherits(&JSFunction::s_info)) { + JSFunction* function = asFunction(*ptr); + if (function->isHostFunction()) + continue; + executable = function->jsExecutable(); + } else + continue; + ASSERT(executable->inherits(&ScriptExecutable::s_info)); + executable->unlinkCalls(); + if (executable->inherits(&FunctionExecutable::s_info)) + recompiler.currentlyExecutingFunctions.add(static_cast<FunctionExecutable*>(executable)); + + } + heap.objectSpace().forEachCell<StackPreservingRecompiler>(recompiler); + } + m_regExpCache->invalidateCode(); + heap.collectAllGarbage(); +} + +void releaseExecutableMemory(JSGlobalData& globalData) +{ + globalData.releaseExecutableMemory(); +} + +#if ENABLE(REGEXP_TRACING) +void JSGlobalData::addRegExpToTrace(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 000000000..1619f9297 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -0,0 +1,386 @@ +/* + * 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 "Intrinsic.h" +#include "DateInstanceCache.h" +#include "ExecutableAllocator.h" +#include "Heap.h" +#include "Strong.h" +#include "JITStubs.h" +#include "JSValue.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 HandleStack; + class IdentifierTable; + class Interpreter; + class JSGlobalObject; + class JSObject; + class Keywords; + class NativeExecutable; + class ParserArena; + 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 + }; + + struct TypedArrayDescriptor { + TypedArrayDescriptor() + : m_classInfo(0) + , m_storageOffset(0) + , m_lengthOffset(0) + { + } + TypedArrayDescriptor(const ClassInfo* classInfo, size_t storageOffset, size_t lengthOffset) + : m_classInfo(classInfo) + , m_storageOffset(storageOffset) + , m_lengthOffset(lengthOffset) + { + } + const ClassInfo* m_classInfo; + size_t m_storageOffset; + size_t m_lengthOffset; + }; + + 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, HeapSize = SmallHeap); + static PassRefPtr<JSGlobalData> createLeaked(ThreadStackType, HeapSize = SmallHeap); + static PassRefPtr<JSGlobalData> createContextGroup(ThreadStackType, HeapSize = SmallHeap); + ~JSGlobalData(); + + void makeUsableFromMultipleThreads() { heap.machineThreads().makeUsableFromMultipleThreads(); } + + GlobalDataType globalDataType; + ClientData* clientData; + CallFrame* topCallFrame; + + const HashTable* arrayConstructorTable; + const HashTable* arrayPrototypeTable; + const HashTable* booleanPrototypeTable; + const HashTable* dateTable; + const HashTable* dateConstructorTable; + const HashTable* errorPrototypeTable; + const HashTable* globalObjectTable; + const HashTable* jsonTable; + const HashTable* mathTable; + const HashTable* numberConstructorTable; + const HashTable* numberPrototypeTable; + const HashTable* objectConstructorTable; + const HashTable* objectPrototypeTable; + const HashTable* regExpTable; + const HashTable* regExpConstructorTable; + const HashTable* regExpPrototypeTable; + const HashTable* stringTable; + const HashTable* stringConstructorTable; + + Strong<Structure> structureStructure; + Strong<Structure> debuggerActivationStructure; + Strong<Structure> activationStructure; + Strong<Structure> interruptedExecutionErrorStructure; + Strong<Structure> terminatedExecutionErrorStructure; + Strong<Structure> staticScopeStructure; + Strong<Structure> strictEvalActivationStructure; + Strong<Structure> stringStructure; + Strong<Structure> notAnObjectStructure; + Strong<Structure> propertyNameIteratorStructure; + Strong<Structure> getterSetterStructure; + Strong<Structure> apiWrapperStructure; + Strong<Structure> scopeChainNodeStructure; + Strong<Structure> executableStructure; + Strong<Structure> nativeExecutableStructure; + Strong<Structure> evalExecutableStructure; + Strong<Structure> programExecutableStructure; + Strong<Structure> functionExecutableStructure; + Strong<Structure> regExpStructure; + Strong<Structure> structureChainStructure; + + 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; + Vector<CodeBlock*> codeBlocksBeingCompiled; + void startedCompiling(CodeBlock* codeBlock) + { + codeBlocksBeingCompiled.append(codeBlock); + } + + void finishedCompiling(CodeBlock* codeBlock) + { + ASSERT_UNUSED(codeBlock, codeBlock == codeBlocksBeingCompiled.last()); + codeBlocksBeingCompiled.removeLast(); + } + +#if ENABLE(ASSEMBLER) + ExecutableAllocator executableAllocator; +#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(); + } + + OwnPtr<ParserArena> parserArena; + OwnPtr<Keywords> keywords; + Interpreter* interpreter; +#if ENABLE(JIT) + OwnPtr<JITThunks> jitStubs; + MacroAssemblerCodeRef getCTIStub(ThunkGenerator generator) + { + return jitStubs->ctiStub(this, generator); + } + NativeExecutable* getHostFunction(NativeFunction, Intrinsic); +#endif + NativeExecutable* getHostFunction(NativeFunction, NativeFunction constructor); + + TimeoutChecker timeoutChecker; + Terminator terminator; + Heap heap; + + JSValue exception; +#if ENABLE(JIT) + ReturnAddressPtr exceptionLocation; + JSValue hostCallReturnValue; +#if ENABLE(DFG_JIT) + uint32_t osrExitIndex; + void* osrExitJumpDestination; + Vector<void*> scratchBuffers; + size_t sizeOfLastScratchBuffer; + + void* scratchBufferForSize(size_t size) + { + if (!size) + return 0; + + if (size > sizeOfLastScratchBuffer) { + // Protect against a N^2 memory usage pathology by ensuring + // that at worst, we get a geometric series, meaning that the + // total memory usage is somewhere around + // max(scratch buffer size) * 4. + sizeOfLastScratchBuffer = size * 2; + + scratchBuffers.append(fastMalloc(sizeOfLastScratchBuffer)); + } + + return scratchBuffers.last(); + } +#endif +#endif + + HashMap<OpaqueJSClass*, OwnPtr<OpaqueJSClassContextData> > opaqueJSClassData; + + JSGlobalObject* dynamicGlobalObject; + + HashSet<JSObject*> stringRecursionCheckVisitedObjects; + + double cachedUTCOffset; + DSTOffsetCache dstOffsetCache; + + UString cachedDateString; + double cachedDateStringValue; + + int maxReentryDepth; + + RegExpCache* m_regExpCache; + BumpPointerAllocator m_regExpAllocator; + +#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(); + void clearBuiltinStructures(); + + bool isCollectorBusy() { return heap.isBusy(); } + void releaseExecutableMemory(); + +#if ENABLE(GC_VALIDATION) + bool isInitializingObject() const; + void setInitializingObject(bool); +#endif + +#if CPU(X86) && ENABLE(JIT) + unsigned m_timeoutCount; +#endif + +#define registerTypedArrayFunction(type, capitalizedType) \ + void registerTypedArrayDescriptor(const capitalizedType##Array*, const TypedArrayDescriptor& descriptor) \ + { \ + ASSERT(!m_##type##ArrayDescriptor.m_classInfo || m_##type##ArrayDescriptor.m_classInfo == descriptor.m_classInfo); \ + m_##type##ArrayDescriptor = descriptor; \ + } \ + const TypedArrayDescriptor& type##ArrayDescriptor() const { return m_##type##ArrayDescriptor; } + + registerTypedArrayFunction(int8, Int8); + registerTypedArrayFunction(int16, Int16); + registerTypedArrayFunction(int32, Int32); + registerTypedArrayFunction(uint8, Uint8); + registerTypedArrayFunction(uint16, Uint16); + registerTypedArrayFunction(uint32, Uint32); + registerTypedArrayFunction(float32, Float32); + registerTypedArrayFunction(float64, Float64); +#undef registerTypedArrayFunction + + private: + JSGlobalData(GlobalDataType, ThreadStackType, HeapSize); + static JSGlobalData*& sharedInstanceInternal(); + void createNativeThunk(); +#if ENABLE(JIT) && ENABLE(INTERPRETER) + bool m_canUseJIT; +#endif + StackBounds m_stack; +#if ENABLE(GC_VALIDATION) + bool m_isInitializingObject; +#endif + TypedArrayDescriptor m_int8ArrayDescriptor; + TypedArrayDescriptor m_int16ArrayDescriptor; + TypedArrayDescriptor m_int32ArrayDescriptor; + TypedArrayDescriptor m_uint8ArrayDescriptor; + TypedArrayDescriptor m_uint16ArrayDescriptor; + TypedArrayDescriptor m_uint32ArrayDescriptor; + TypedArrayDescriptor m_float32ArrayDescriptor; + TypedArrayDescriptor m_float64ArrayDescriptor; + }; + +#if ENABLE(GC_VALIDATION) + inline bool JSGlobalData::isInitializingObject() const + { + return m_isInitializingObject; + } + + inline void JSGlobalData::setInitializingObject(bool initializingObject) + { + m_isInitializingObject = initializingObject; + } +#endif + +} // namespace JSC + +#endif // JSGlobalData_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp new file mode 100644 index 000000000..5ad53c222 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -0,0 +1,482 @@ +/* + * 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 "Error.h" +#include "ErrorConstructor.h" +#include "ErrorPrototype.h" +#include "FunctionConstructor.h" +#include "FunctionPrototype.h" +#include "GetterSetter.h" +#include "JSBoundFunction.h" +#include "JSFunction.h" +#include "JSGlobalObjectFunctions.h" +#include "JSLock.h" +#include "JSONObject.h" +#include "Interpreter.h" +#include "Lookup.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 "RegExpConstructor.h" +#include "RegExpMatchesArray.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "ScopeChainMark.h" +#include "StringConstructor.h" +#include "StringPrototype.h" +#include "Debugger.h" + +#include "JSGlobalObject.lut.h" + +namespace JSC { + +const ClassInfo JSGlobalObject::s_info = { "GlobalObject", &JSVariableObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(JSGlobalObject) }; + +const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript }; + +/* Source for JSGlobalObject.lut.h +@begin globalObjectTable + parseInt globalFuncParseInt DontEnum|Function 2 + parseFloat globalFuncParseFloat DontEnum|Function 1 + isNaN globalFuncIsNaN DontEnum|Function 1 + isFinite globalFuncIsFinite DontEnum|Function 1 + escape globalFuncEscape DontEnum|Function 1 + unescape globalFuncUnescape DontEnum|Function 1 + decodeURI globalFuncDecodeURI DontEnum|Function 1 + decodeURIComponent globalFuncDecodeURIComponent DontEnum|Function 1 + encodeURI globalFuncEncodeURI DontEnum|Function 1 + encodeURIComponent globalFuncEncodeURIComponent DontEnum|Function 1 +@end +*/ + +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; + +template <typename T> static inline void visitIfNeeded(SlotVisitor& visitor, WriteBarrier<T>* v) +{ + if (*v) + visitor.append(v); +} + +JSGlobalObject::~JSGlobalObject() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (m_debugger) + m_debugger->detach(this); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (UNLIKELY(*profiler != 0)) { + (*profiler)->stopProfiling(this); + } +} + +void JSGlobalObject::destroy(JSCell* cell) +{ + jsCast<JSGlobalObject*>(cell)->JSGlobalObject::~JSGlobalObject(); +} + +void JSGlobalObject::init(JSObject* thisValue) +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + structure()->disableSpecificFunctionTracking(); + + m_globalData = Heap::heap(this)->globalData(); + m_globalScopeChain.set(*m_globalData, this, ScopeChainNode::create(0, this, m_globalData.get(), this, thisValue)); + + JSGlobalObject::globalExec()->init(0, 0, m_globalScopeChain.get(), CallFrame::noCaller(), 0, 0); + + m_debugger = 0; + + reset(prototype()); +} + +void JSGlobalObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(cell); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + + if (thisObject->symbolTablePut(exec, propertyName, value, slot.isStrictMode())) + return; + JSVariableObject::put(thisObject, exec, propertyName, value, slot); +} + +void JSGlobalObject::putWithAttributes(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(object); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + + if (thisObject->symbolTablePutWithAttributes(exec->globalData(), propertyName, value, attributes)) + return; + + JSValue valueBefore = thisObject->getDirect(exec->globalData(), propertyName); + PutPropertySlot slot; + JSVariableObject::put(thisObject, exec, propertyName, value, slot); + if (!valueBefore) { + JSValue valueAfter = thisObject->getDirect(exec->globalData(), propertyName); + if (valueAfter) + JSObject::putWithAttributes(thisObject, exec, propertyName, valueAfter, attributes); + } +} + +void JSGlobalObject::defineGetter(JSObject* object, ExecState* exec, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(object); + PropertySlot slot; + if (!thisObject->symbolTableGet(propertyName, slot)) + JSVariableObject::defineGetter(thisObject, exec, propertyName, getterFunc, attributes); +} + +void JSGlobalObject::defineSetter(JSObject* object, ExecState* exec, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(object); + PropertySlot slot; + if (!thisObject->symbolTableGet(propertyName, slot)) + JSVariableObject::defineSetter(thisObject, 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(); + + m_functionPrototype.set(exec->globalData(), this, FunctionPrototype::create(exec, this, FunctionPrototype::createStructure(exec->globalData(), this, jsNull()))); // The real prototype will be set once ObjectPrototype is created. + m_functionStructure.set(exec->globalData(), this, JSFunction::createStructure(exec->globalData(), this, m_functionPrototype.get())); + m_boundFunctionStructure.set(exec->globalData(), this, JSBoundFunction::createStructure(exec->globalData(), this, m_functionPrototype.get())); + m_namedFunctionStructure.set(exec->globalData(), this, Structure::addPropertyTransition(exec->globalData(), m_functionStructure.get(), exec->globalData().propertyNames->name, DontDelete | ReadOnly | DontEnum, 0, m_functionNameOffset)); + m_internalFunctionStructure.set(exec->globalData(), this, InternalFunction::createStructure(exec->globalData(), this, m_functionPrototype.get())); + JSFunction* callFunction = 0; + JSFunction* applyFunction = 0; + m_functionPrototype->addFunctionProperties(exec, this, &callFunction, &applyFunction); + m_callFunction.set(exec->globalData(), this, callFunction); + m_applyFunction.set(exec->globalData(), this, applyFunction); + m_objectPrototype.set(exec->globalData(), this, ObjectPrototype::create(exec, this, ObjectPrototype::createStructure(exec->globalData(), this, jsNull()))); + m_functionPrototype->structure()->setPrototypeWithoutTransition(exec->globalData(), m_objectPrototype.get()); + + m_emptyObjectStructure.set(exec->globalData(), this, m_objectPrototype->inheritorID(exec->globalData())); + m_nullPrototypeObjectStructure.set(exec->globalData(), this, createEmptyObjectStructure(exec->globalData(), this, jsNull())); + + m_callbackFunctionStructure.set(exec->globalData(), this, JSCallbackFunction::createStructure(exec->globalData(), this, m_functionPrototype.get())); + m_argumentsStructure.set(exec->globalData(), this, Arguments::createStructure(exec->globalData(), this, m_objectPrototype.get())); + m_callbackConstructorStructure.set(exec->globalData(), this, JSCallbackConstructor::createStructure(exec->globalData(), this, m_objectPrototype.get())); + m_callbackObjectStructure.set(exec->globalData(), this, JSCallbackObject<JSNonFinalObject>::createStructure(exec->globalData(), this, m_objectPrototype.get())); + + m_arrayPrototype.set(exec->globalData(), this, ArrayPrototype::create(exec, this, ArrayPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); + m_arrayStructure.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get())); + m_regExpMatchesArrayStructure.set(exec->globalData(), this, RegExpMatchesArray::createStructure(exec->globalData(), this, m_arrayPrototype.get())); + + m_stringPrototype.set(exec->globalData(), this, StringPrototype::create(exec, this, StringPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); + m_stringObjectStructure.set(exec->globalData(), this, StringObject::createStructure(exec->globalData(), this, m_stringPrototype.get())); + + m_booleanPrototype.set(exec->globalData(), this, BooleanPrototype::create(exec, this, BooleanPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); + m_booleanObjectStructure.set(exec->globalData(), this, BooleanObject::createStructure(exec->globalData(), this, m_booleanPrototype.get())); + + m_numberPrototype.set(exec->globalData(), this, NumberPrototype::create(exec, this, NumberPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); + m_numberObjectStructure.set(exec->globalData(), this, NumberObject::createStructure(exec->globalData(), this, m_numberPrototype.get())); + + m_datePrototype.set(exec->globalData(), this, DatePrototype::create(exec, this, DatePrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); + m_dateStructure.set(exec->globalData(), this, DateInstance::createStructure(exec->globalData(), this, m_datePrototype.get())); + + RegExp* emptyRegex = RegExp::create(exec->globalData(), "", NoFlags); + + m_regExpPrototype.set(exec->globalData(), this, RegExpPrototype::create(exec, this, RegExpPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()), emptyRegex)); + m_regExpStructure.set(exec->globalData(), this, RegExpObject::createStructure(exec->globalData(), this, m_regExpPrototype.get())); + + m_methodCallDummy.set(exec->globalData(), this, constructEmptyObject(exec)); + + ErrorPrototype* errorPrototype = ErrorPrototype::create(exec, this, ErrorPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get())); + m_errorStructure.set(exec->globalData(), this, ErrorInstance::createStructure(exec->globalData(), this, errorPrototype)); + + // Constructors + + JSCell* objectConstructor = ObjectConstructor::create(exec, this, ObjectConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_objectPrototype.get()); + JSCell* functionConstructor = FunctionConstructor::create(exec, this, FunctionConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_functionPrototype.get()); + JSCell* arrayConstructor = ArrayConstructor::create(exec, this, ArrayConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_arrayPrototype.get()); + JSCell* stringConstructor = StringConstructor::create(exec, this, StringConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_stringPrototype.get()); + JSCell* booleanConstructor = BooleanConstructor::create(exec, this, BooleanConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_booleanPrototype.get()); + JSCell* numberConstructor = NumberConstructor::create(exec, this, NumberConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_numberPrototype.get()); + JSCell* dateConstructor = DateConstructor::create(exec, this, DateConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_datePrototype.get()); + + m_regExpConstructor.set(exec->globalData(), this, RegExpConstructor::create(exec, this, RegExpConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), m_regExpPrototype.get())); + + m_errorConstructor.set(exec->globalData(), this, ErrorConstructor::create(exec, this, ErrorConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()), errorPrototype)); + + Structure* nativeErrorPrototypeStructure = NativeErrorPrototype::createStructure(exec->globalData(), this, errorPrototype); + Structure* nativeErrorStructure = NativeErrorConstructor::createStructure(exec->globalData(), this, m_functionPrototype.get()); + m_evalErrorConstructor.set(exec->globalData(), this, NativeErrorConstructor::create(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "EvalError")); + m_rangeErrorConstructor.set(exec->globalData(), this, NativeErrorConstructor::create(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "RangeError")); + m_referenceErrorConstructor.set(exec->globalData(), this, NativeErrorConstructor::create(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "ReferenceError")); + m_syntaxErrorConstructor.set(exec->globalData(), this, NativeErrorConstructor::create(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "SyntaxError")); + m_typeErrorConstructor.set(exec->globalData(), this, NativeErrorConstructor::create(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "TypeError")); + m_URIErrorConstructor.set(exec->globalData(), this, NativeErrorConstructor::create(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "URIError")); + + m_objectPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, objectConstructor, DontEnum); + m_functionPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, functionConstructor, DontEnum); + m_arrayPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, arrayConstructor, DontEnum); + m_booleanPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, booleanConstructor, DontEnum); + m_stringPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, stringConstructor, DontEnum); + m_numberPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, numberConstructor, DontEnum); + m_datePrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, dateConstructor, DontEnum); + m_regExpPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, m_regExpConstructor.get(), DontEnum); + errorPrototype->putDirectWithoutTransition(exec->globalData(), exec->propertyNames().constructor, m_errorConstructor.get(), DontEnum); + + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Object"), objectConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Function"), functionConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Array"), arrayConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Boolean"), booleanConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "String"), stringConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Number"), numberConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Date"), dateConstructor, DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "RegExp"), m_regExpConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "Error"), m_errorConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "EvalError"), m_evalErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "RangeError"), m_rangeErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "ReferenceError"), m_referenceErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "SyntaxError"), m_syntaxErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "TypeError"), m_typeErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "URIError"), m_URIErrorConstructor.get(), DontEnum); + + m_evalFunction.set(exec->globalData(), this, JSFunction::create(exec, this, 1, exec->propertyNames().eval, globalFuncEval)); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().eval, m_evalFunction.get(), DontEnum); + + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "JSON"), JSONObject::create(exec, this, JSONObject::createStructure(exec->globalData(), this, m_objectPrototype.get())), DontEnum); + + GlobalPropertyInfo staticGlobals[] = { + GlobalPropertyInfo(Identifier(exec, "Math"), MathObject::create(exec, this, MathObject::createStructure(exec->globalData(), this, m_objectPrototype.get())), DontEnum | DontDelete), + GlobalPropertyInfo(Identifier(exec, "NaN"), jsNaN(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "Infinity"), jsNumber(std::numeric_limits<double>::infinity()), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete | ReadOnly) + }; + addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); + + resetPrototype(exec->globalData(), prototype); +} + +void JSGlobalObject::createThrowTypeError(ExecState* exec) +{ + JSFunction* thrower = JSFunction::create(exec, this, 0, Identifier(), globalFuncThrowTypeError); + GetterSetter* getterSetter = GetterSetter::create(exec); + getterSetter->setGetter(exec->globalData(), thrower); + getterSetter->setSetter(exec->globalData(), thrower); + m_throwTypeErrorGetterSetter.set(exec->globalData(), this, getterSetter); +} + +// Set prototype, and also insert the object prototype at the end of the chain. +void JSGlobalObject::resetPrototype(JSGlobalData& globalData, JSValue prototype) +{ + setPrototype(globalData, prototype); + + JSObject* oldLastInPrototypeChain = lastInPrototypeChain(this); + JSObject* objectPrototype = m_objectPrototype.get(); + if (oldLastInPrototypeChain != objectPrototype) + oldLastInPrototypeChain->setPrototype(globalData, objectPrototype); +} + +void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + JSVariableObject::visitChildren(thisObject, visitor); + + visitIfNeeded(visitor, &thisObject->m_globalScopeChain); + visitIfNeeded(visitor, &thisObject->m_methodCallDummy); + + visitIfNeeded(visitor, &thisObject->m_regExpConstructor); + visitIfNeeded(visitor, &thisObject->m_errorConstructor); + visitIfNeeded(visitor, &thisObject->m_evalErrorConstructor); + visitIfNeeded(visitor, &thisObject->m_rangeErrorConstructor); + visitIfNeeded(visitor, &thisObject->m_referenceErrorConstructor); + visitIfNeeded(visitor, &thisObject->m_syntaxErrorConstructor); + visitIfNeeded(visitor, &thisObject->m_typeErrorConstructor); + visitIfNeeded(visitor, &thisObject->m_URIErrorConstructor); + + visitIfNeeded(visitor, &thisObject->m_evalFunction); + visitIfNeeded(visitor, &thisObject->m_callFunction); + visitIfNeeded(visitor, &thisObject->m_applyFunction); + visitIfNeeded(visitor, &thisObject->m_throwTypeErrorGetterSetter); + + visitIfNeeded(visitor, &thisObject->m_objectPrototype); + visitIfNeeded(visitor, &thisObject->m_functionPrototype); + visitIfNeeded(visitor, &thisObject->m_arrayPrototype); + visitIfNeeded(visitor, &thisObject->m_booleanPrototype); + visitIfNeeded(visitor, &thisObject->m_stringPrototype); + visitIfNeeded(visitor, &thisObject->m_numberPrototype); + visitIfNeeded(visitor, &thisObject->m_datePrototype); + visitIfNeeded(visitor, &thisObject->m_regExpPrototype); + + visitIfNeeded(visitor, &thisObject->m_argumentsStructure); + visitIfNeeded(visitor, &thisObject->m_arrayStructure); + visitIfNeeded(visitor, &thisObject->m_booleanObjectStructure); + visitIfNeeded(visitor, &thisObject->m_callbackConstructorStructure); + visitIfNeeded(visitor, &thisObject->m_callbackFunctionStructure); + visitIfNeeded(visitor, &thisObject->m_callbackObjectStructure); + visitIfNeeded(visitor, &thisObject->m_dateStructure); + visitIfNeeded(visitor, &thisObject->m_emptyObjectStructure); + visitIfNeeded(visitor, &thisObject->m_nullPrototypeObjectStructure); + visitIfNeeded(visitor, &thisObject->m_errorStructure); + visitIfNeeded(visitor, &thisObject->m_functionStructure); + visitIfNeeded(visitor, &thisObject->m_boundFunctionStructure); + visitIfNeeded(visitor, &thisObject->m_namedFunctionStructure); + visitIfNeeded(visitor, &thisObject->m_numberObjectStructure); + visitIfNeeded(visitor, &thisObject->m_regExpMatchesArrayStructure); + visitIfNeeded(visitor, &thisObject->m_regExpStructure); + visitIfNeeded(visitor, &thisObject->m_stringObjectStructure); + visitIfNeeded(visitor, &thisObject->m_internalFunctionStructure); + + if (thisObject->m_registerArray) { + // Outside the execution of global code, when our variables are torn off, + // we can mark the torn-off array. + visitor.appendValues(thisObject->m_registerArray.get(), thisObject->m_registerArraySize); + } else if (thisObject->m_registers) { + // During execution of global code, when our variables are in the register file, + // the symbol table tells us how many variables there are, and registers + // points to where they end, and the registers used for execution begin. + visitor.appendValues(thisObject->m_registers - thisObject->symbolTable().size(), thisObject->symbolTable().size()); + } +} + +ExecState* JSGlobalObject::globalExec() +{ + return CallFrame::create(m_globalCallFrame + RegisterFile::CallFrameHeaderSize); +} + +void JSGlobalObject::resizeRegisters(size_t newSize) +{ + // Previous duplicate symbols may have created spare capacity in m_registerArray. + if (newSize <= m_registerArraySize) + return; + + size_t oldSize = m_registerArraySize; + OwnArrayPtr<WriteBarrier<Unknown> > registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[newSize]); + for (size_t i = 0; i < oldSize; ++i) + registerArray[i].set(globalData(), this, m_registerArray[i].get()); + for (size_t i = oldSize; i < newSize; ++i) + registerArray[i].setUndefined(); + + WriteBarrier<Unknown>* registers = registerArray.get(); + setRegisters(registers, registerArray.release(), newSize); +} + +void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count) +{ + resizeRegisters(symbolTable().size() + count); + + for (int i = 0; i < count; ++i) { + GlobalPropertyInfo& global = globals[i]; + ASSERT(global.attributes & DontDelete); + + int index = symbolTable().size(); + SymbolTableEntry newEntry(index, global.attributes); + symbolTable().add(global.identifier.impl(), newEntry); + registerAt(index).set(globalData(), this, global.value); + } +} + +bool JSGlobalObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(cell); + if (getStaticFunctionSlot<JSVariableObject>(exec, ExecState::globalObjectTable(exec), thisObject, propertyName, slot)) + return true; + return thisObject->symbolTableGet(propertyName, slot); +} + +bool JSGlobalObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(object); + if (getStaticFunctionDescriptor<JSVariableObject>(exec, ExecState::globalObjectTable(exec), thisObject, propertyName, descriptor)) + return true; + return thisObject->symbolTableGet(propertyName, descriptor); +} + +void JSGlobalObject::clearRareData(JSCell* cell) +{ + jsCast<JSGlobalObject*>(cell)->m_rareData.clear(); +} + +DynamicGlobalObjectScope::DynamicGlobalObjectScope(JSGlobalData& globalData, JSGlobalObject* dynamicGlobalObject) + : m_dynamicGlobalObjectSlot(globalData.dynamicGlobalObject) + , m_savedDynamicGlobalObject(m_dynamicGlobalObjectSlot) +{ + if (!m_dynamicGlobalObjectSlot) { +#if ENABLE(ASSEMBLER) + if (ExecutableAllocator::underMemoryPressure()) + globalData.recompileAllJSFunctions(); +#endif + + m_dynamicGlobalObjectSlot = dynamicGlobalObject; + + // Reset the date cache between JS invocations to force the VM + // to observe time zone changes. + globalData.resetDateCache(); + } +} + +void slowValidateCell(JSGlobalObject* globalObject) +{ + if (!globalObject->isGlobalObject()) + CRASH(); + ASSERT_GC_OBJECT_INHERITS(globalObject, &JSGlobalObject::s_info); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h new file mode 100644 index 000000000..1caa8b187 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -0,0 +1,510 @@ +/* + * 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 "JSGlobalThis.h" +#include "JSVariableObject.h" +#include "JSWeakObjectMapRefInternal.h" +#include "NumberPrototype.h" +#include "StringPrototype.h" +#include "StructureChain.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 GetterSetter; + class GlobalCodeBlock; + class NativeErrorConstructor; + class ProgramCodeBlock; + class RegExpConstructor; + class RegExpPrototype; + class RegisterFile; + + struct ActivationStackNode; + struct HashTable; + + typedef Vector<ExecState*, 16> ExecStateStack; + + struct GlobalObjectMethodTable { + typedef bool (*SupportsProfilingFunctionPtr)(const JSGlobalObject*); + SupportsProfilingFunctionPtr supportsProfiling; + + typedef bool (*SupportsRichSourceInfoFunctionPtr)(const JSGlobalObject*); + SupportsRichSourceInfoFunctionPtr supportsRichSourceInfo; + + typedef bool (*ShouldInterruptScriptFunctionPtr)(const JSGlobalObject*); + ShouldInterruptScriptFunctionPtr shouldInterruptScript; + }; + + class JSGlobalObject : public JSVariableObject { + private: + typedef HashSet<RefPtr<OpaqueJSWeakObjectMap> > WeakMapSet; + + struct JSGlobalObjectRareData { + JSGlobalObjectRareData() + : profileGroup(0) + { + } + + WeakMapSet weakMaps; + unsigned profileGroup; + }; + + protected: + + RefPtr<JSGlobalData> m_globalData; + + size_t m_registerArraySize; + Register m_globalCallFrame[RegisterFile::CallFrameHeaderSize]; + + WriteBarrier<ScopeChainNode> m_globalScopeChain; + WriteBarrier<JSObject> m_methodCallDummy; + + WriteBarrier<RegExpConstructor> m_regExpConstructor; + WriteBarrier<ErrorConstructor> m_errorConstructor; + WriteBarrier<NativeErrorConstructor> m_evalErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_rangeErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_referenceErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_syntaxErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_typeErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_URIErrorConstructor; + + WriteBarrier<JSFunction> m_evalFunction; + WriteBarrier<JSFunction> m_callFunction; + WriteBarrier<JSFunction> m_applyFunction; + WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter; + + WriteBarrier<ObjectPrototype> m_objectPrototype; + WriteBarrier<FunctionPrototype> m_functionPrototype; + WriteBarrier<ArrayPrototype> m_arrayPrototype; + WriteBarrier<BooleanPrototype> m_booleanPrototype; + WriteBarrier<StringPrototype> m_stringPrototype; + WriteBarrier<NumberPrototype> m_numberPrototype; + WriteBarrier<DatePrototype> m_datePrototype; + WriteBarrier<RegExpPrototype> m_regExpPrototype; + + WriteBarrier<Structure> m_argumentsStructure; + WriteBarrier<Structure> m_arrayStructure; + WriteBarrier<Structure> m_booleanObjectStructure; + WriteBarrier<Structure> m_callbackConstructorStructure; + WriteBarrier<Structure> m_callbackFunctionStructure; + WriteBarrier<Structure> m_callbackObjectStructure; + WriteBarrier<Structure> m_dateStructure; + WriteBarrier<Structure> m_emptyObjectStructure; + WriteBarrier<Structure> m_nullPrototypeObjectStructure; + WriteBarrier<Structure> m_errorStructure; + WriteBarrier<Structure> m_functionStructure; + WriteBarrier<Structure> m_boundFunctionStructure; + WriteBarrier<Structure> m_namedFunctionStructure; + size_t m_functionNameOffset; + WriteBarrier<Structure> m_numberObjectStructure; + WriteBarrier<Structure> m_regExpMatchesArrayStructure; + WriteBarrier<Structure> m_regExpStructure; + WriteBarrier<Structure> m_stringObjectStructure; + WriteBarrier<Structure> m_internalFunctionStructure; + + Debugger* m_debugger; + + OwnPtr<JSGlobalObjectRareData> m_rareData; + + WeakRandom m_weakRandom; + + SymbolTable m_symbolTable; + + bool m_evalEnabled; + + static JS_EXPORTDATA const GlobalObjectMethodTable s_globalObjectMethodTable; + const GlobalObjectMethodTable* m_globalObjectMethodTable; + + void createRareDataIfNeeded() + { + if (m_rareData) + return; + m_rareData = adoptPtr(new JSGlobalObjectRareData); + Heap::heap(this)->addFinalizer(this, clearRareData); + } + + public: + typedef JSVariableObject Base; + + static JSGlobalObject* create(JSGlobalData& globalData, Structure* structure) + { + JSGlobalObject* globalObject = new (NotNull, allocateCell<JSGlobalObject>(globalData.heap)) JSGlobalObject(globalData, structure); + globalObject->finishCreation(globalData); + return globalObject; + } + + static JS_EXPORTDATA const ClassInfo s_info; + + protected: + explicit JSGlobalObject(JSGlobalData& globalData, Structure* structure, const GlobalObjectMethodTable* globalObjectMethodTable = 0) + : JSVariableObject(globalData, structure, &m_symbolTable, 0) + , m_registerArraySize(0) + , m_globalScopeChain() + , m_weakRandom(static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0))) + , m_evalEnabled(true) + , m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + structure()->setGlobalObject(globalData, this); + init(this); + } + + void finishCreation(JSGlobalData& globalData, JSGlobalThis* thisValue) + { + Base::finishCreation(globalData); + structure()->setGlobalObject(globalData, this); + init(thisValue); + } + + public: + ~JSGlobalObject(); + static void destroy(JSCell*); + + static void visitChildren(JSCell*, SlotVisitor&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + bool hasOwnPropertyForWrite(ExecState*, const Identifier&); + static void put(JSCell*, ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + static void putWithAttributes(JSObject*, ExecState*, const Identifier& propertyName, JSValue, unsigned attributes); + + static void defineGetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes); + static void defineSetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes); + + // We use this in the code generator as we perform symbol table + // lookups prior to initializing the properties + bool symbolTableHasProperty(const Identifier& propertyName); + + // The following accessors return pristine values, even if a script + // replaces the global object's associated property. + + RegExpConstructor* regExpConstructor() const { return m_regExpConstructor.get(); } + + ErrorConstructor* errorConstructor() const { return m_errorConstructor.get(); } + NativeErrorConstructor* evalErrorConstructor() const { return m_evalErrorConstructor.get(); } + NativeErrorConstructor* rangeErrorConstructor() const { return m_rangeErrorConstructor.get(); } + NativeErrorConstructor* referenceErrorConstructor() const { return m_referenceErrorConstructor.get(); } + NativeErrorConstructor* syntaxErrorConstructor() const { return m_syntaxErrorConstructor.get(); } + NativeErrorConstructor* typeErrorConstructor() const { return m_typeErrorConstructor.get(); } + NativeErrorConstructor* URIErrorConstructor() const { return m_URIErrorConstructor.get(); } + + JSFunction* evalFunction() const { return m_evalFunction.get(); } + JSFunction* callFunction() const { return m_callFunction.get(); } + JSFunction* applyFunction() const { return m_applyFunction.get(); } + GetterSetter* throwTypeErrorGetterSetter(ExecState* exec) + { + if (!m_throwTypeErrorGetterSetter) + createThrowTypeError(exec); + return m_throwTypeErrorGetterSetter.get(); + } + + ObjectPrototype* objectPrototype() const { return m_objectPrototype.get(); } + FunctionPrototype* functionPrototype() const { return m_functionPrototype.get(); } + ArrayPrototype* arrayPrototype() const { return m_arrayPrototype.get(); } + BooleanPrototype* booleanPrototype() const { return m_booleanPrototype.get(); } + StringPrototype* stringPrototype() const { return m_stringPrototype.get(); } + NumberPrototype* numberPrototype() const { return m_numberPrototype.get(); } + DatePrototype* datePrototype() const { return m_datePrototype.get(); } + RegExpPrototype* regExpPrototype() const { return m_regExpPrototype.get(); } + + JSObject* methodCallDummy() const { return m_methodCallDummy.get(); } + + Structure* argumentsStructure() const { return m_argumentsStructure.get(); } + Structure* arrayStructure() const { return m_arrayStructure.get(); } + Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); } + Structure* callbackConstructorStructure() const { return m_callbackConstructorStructure.get(); } + Structure* callbackFunctionStructure() const { return m_callbackFunctionStructure.get(); } + Structure* callbackObjectStructure() const { return m_callbackObjectStructure.get(); } + Structure* dateStructure() const { return m_dateStructure.get(); } + Structure* emptyObjectStructure() const { return m_emptyObjectStructure.get(); } + Structure* nullPrototypeObjectStructure() const { return m_nullPrototypeObjectStructure.get(); } + Structure* errorStructure() const { return m_errorStructure.get(); } + Structure* functionStructure() const { return m_functionStructure.get(); } + Structure* boundFunctionStructure() const { return m_boundFunctionStructure.get(); } + Structure* namedFunctionStructure() const { return m_namedFunctionStructure.get(); } + size_t functionNameOffset() const { return m_functionNameOffset; } + Structure* numberObjectStructure() const { return m_numberObjectStructure.get(); } + Structure* internalFunctionStructure() const { return m_internalFunctionStructure.get(); } + Structure* regExpMatchesArrayStructure() const { return m_regExpMatchesArrayStructure.get(); } + Structure* regExpStructure() const { return m_regExpStructure.get(); } + Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); } + + void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; } + unsigned profileGroup() const + { + if (!m_rareData) + return 0; + return m_rareData->profileGroup; + } + + Debugger* debugger() const { return m_debugger; } + void setDebugger(Debugger* debugger) { m_debugger = debugger; } + + const GlobalObjectMethodTable* globalObjectMethodTable() const { return m_globalObjectMethodTable; } + + static bool supportsProfiling(const JSGlobalObject*) { return false; } + static bool supportsRichSourceInfo(const JSGlobalObject*) { return true; } + + ScopeChainNode* globalScopeChain() { return m_globalScopeChain.get(); } + + ExecState* globalExec(); + + static bool shouldInterruptScript(const JSGlobalObject*) { return true; } + + bool isDynamicScope(bool& requiresDynamicChecks) const; + + void setEvalEnabled(bool enabled) { m_evalEnabled = enabled; } + bool evalEnabled() { return m_evalEnabled; } + + void resizeRegisters(size_t newSize); + + void resetPrototype(JSGlobalData&, JSValue prototype); + + JSGlobalData& globalData() const { return *m_globalData.get(); } + + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), &s_info); + } + + void registerWeakMap(OpaqueJSWeakObjectMap* map) + { + createRareDataIfNeeded(); + m_rareData->weakMaps.add(map); + } + + void unregisterWeakMap(OpaqueJSWeakObjectMap* map) + { + if (m_rareData) + m_rareData->weakMaps.remove(map); + } + + double weakRandomNumber() { return m_weakRandom.get(); } + protected: + + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | 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: + // FIXME: Fold reset into init. + void init(JSObject* thisValue); + void reset(JSValue prototype); + + void createThrowTypeError(ExecState*); + + void setRegisters(WriteBarrier<Unknown>* registers, PassOwnArrayPtr<WriteBarrier<Unknown> > registerArray, size_t count); + static void clearRareData(JSCell*); + }; + + JSGlobalObject* asGlobalObject(JSValue); + + inline JSGlobalObject* asGlobalObject(JSValue value) + { + ASSERT(asObject(value)->isGlobalObject()); + return static_cast<JSGlobalObject*>(asObject(value)); + } + + inline void JSGlobalObject::setRegisters(WriteBarrier<Unknown>* registers, PassOwnArrayPtr<WriteBarrier<Unknown> > registerArray, size_t count) + { + JSVariableObject::setRegisters(registers, registerArray); + m_registerArraySize = count; + } + + inline bool JSGlobalObject::hasOwnPropertyForWrite(ExecState* exec, const Identifier& propertyName) + { + PropertySlot slot; + if (JSVariableObject::getOwnPropertySlot(this, exec, propertyName, slot)) + return true; + bool slotIsWriteable; + return symbolTableGet(propertyName, slot, slotIsWriteable); + } + + inline bool JSGlobalObject::symbolTableHasProperty(const Identifier& propertyName) + { + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + return !entry.isNull(); + } + + inline JSValue Structure::prototypeForLookup(ExecState* exec) const + { + if (isObject()) + return m_prototype.get(); + + 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.set(exec->globalData(), this, StructureChain::create(exec->globalData(), 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); + WriteBarrier<Structure>* cachedStructure = cachedPrototypeChain->head(); + while(*cachedStructure && !prototype.isNull()) { + if (asObject(prototype)->structure() != cachedStructure->get()) + 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, JSGlobalObject* globalObject) + { + return constructEmptyObject(exec, globalObject->emptyObjectStructure()); + } + + inline JSObject* constructEmptyObject(ExecState* exec) + { + return constructEmptyObject(exec, exec->lexicalGlobalObject()); + } + + inline JSArray* constructEmptyArray(ExecState* exec, JSGlobalObject* globalObject, unsigned initialLength = 0) + { + return JSArray::create(exec->globalData(), globalObject->arrayStructure(), initialLength); + } + + inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength = 0) + { + return constructEmptyArray(exec, exec->lexicalGlobalObject(), initialLength); + } + + inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const ArgList& values) + { + JSGlobalData& globalData = exec->globalData(); + unsigned length = values.size(); + JSArray* array = JSArray::tryCreateUninitialized(globalData, globalObject->arrayStructure(), length); + + // FIXME: we should probably throw an out of memory error here, but + // when making this change we should check that all clients of this + // function will correctly handle an exception being thrown from here. + if (!array) + CRASH(); + + for (unsigned i = 0; i < length; ++i) + array->initializeIndex(globalData, i, values.at(i)); + array->completeInitialization(length); + return array; + } + + inline JSArray* constructArray(ExecState* exec, const ArgList& values) + { + return constructArray(exec, exec->lexicalGlobalObject(), values); + } + + inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const JSValue* values, unsigned length) + { + JSGlobalData& globalData = exec->globalData(); + JSArray* array = JSArray::tryCreateUninitialized(globalData, globalObject->arrayStructure(), length); + + // FIXME: we should probably throw an out of memory error here, but + // when making this change we should check that all clients of this + // function will correctly handle an exception being thrown from here. + if (!array) + CRASH(); + + for (unsigned i = 0; i < length; ++i) + array->initializeIndex(globalData, i, values[i]); + array->completeInitialization(length); + return array; + } + + inline JSArray* constructArray(ExecState* exec, const JSValue* values, unsigned length) + { + return constructArray(exec, exec->lexicalGlobalObject(), values, length); + } + + class DynamicGlobalObjectScope { + WTF_MAKE_NONCOPYABLE(DynamicGlobalObjectScope); + public: + DynamicGlobalObjectScope(JSGlobalData&, JSGlobalObject*); + + ~DynamicGlobalObjectScope() + { + m_dynamicGlobalObjectSlot = m_savedDynamicGlobalObject; + } + + private: + JSGlobalObject*& m_dynamicGlobalObjectSlot; + JSGlobalObject* m_savedDynamicGlobalObject; + }; + + inline bool JSGlobalObject::isDynamicScope(bool&) const + { + return true; + } + +} // namespace JSC + +#endif // JSGlobalObject_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp new file mode 100644 index 000000000..bf6b31ef1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -0,0 +1,718 @@ +/* + * 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 "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); +} + +template <typename CharType> +ALWAYS_INLINE +static JSValue decode(ExecState* exec, const CharType* characters, int length, const char* doNotUnescape, bool strict) +{ + JSStringBuilder builder; + int k = 0; + UChar u = 0; + while (k < length) { + const CharType* p = characters + k; + CharType c = *p; + if (c == '%') { + int charLen = 0; + if (k <= length - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) { + const char b0 = Lexer<CharType>::convertHex(p[1], p[2]); + const int sequenceLen = UTF8SequenceLength(b0); + if (sequenceLen && k <= length - sequenceLen * 3) { + charLen = sequenceLen * 3; + char sequence[5]; + sequence[0] = b0; + for (int i = 1; i < sequenceLen; ++i) { + const CharType* q = p + i * 3; + if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2])) + sequence[i] = Lexer<CharType>::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 <= length - 6 && p[1] == 'u' + && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3]) + && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) { + charLen = 6; + u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]); + } + } + if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) { + if (u < 256) + builder.append(static_cast<LChar>(u)); + else + builder.append(u); + k += charLen; + continue; + } + } + k++; + builder.append(c); + } + return builder.build(exec); +} + +static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) +{ + JSStringBuilder builder; + UString str = exec->argument(0).toString(exec); + + if (str.is8Bit()) + return decode(exec, str.characters8(), str.length(), doNotUnescape, strict); + return decode(exec, str.characters16(), str.length(), doNotUnescape, strict); +} + +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 LChar* s, int length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const LChar* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == std::numeric_limits<double>::infinity()) { + if (*p != '0') { + number = std::numeric_limits<double>::infinity(); + 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 == std::numeric_limits<double>::infinity()) { + if (*p != '0') { + number = std::numeric_limits<double>::infinity(); + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + +// ES5.1 15.1.2.2 +template <typename CharType> +ALWAYS_INLINE +static double parseInt(const UString& s, const CharType* data, int radix) +{ + // 1. Let inputString be ToString(string). + // 2. Let S be a newly created substring of inputString consisting of the first character that is not a + // StrWhiteSpaceChar and all characters following that character. (In other words, remove leading white + // space.) If inputString does not contain any such characters, let S be the empty string. + int length = s.length(); + int p = 0; + while (p < length && isStrWhiteSpace(data[p])) + ++p; + + // 3. Let sign be 1. + // 4. If S is not empty and the first character of S is a minus sign -, let sign be -1. + // 5. If S is not empty and the first character of S is a plus sign + or a minus sign -, then remove the first character from S. + double sign = 1; + if (p < length) { + if (data[p] == '+') + ++p; + else if (data[p] == '-') { + sign = -1; + ++p; + } + } + + // 6. Let R = ToInt32(radix). + // 7. Let stripPrefix be true. + // 8. If R != 0,then + // b. If R != 16, let stripPrefix be false. + // 9. Else, R == 0 + // a. LetR = 10. + // 10. If stripPrefix is true, then + // a. If the length of S is at least 2 and the first two characters of S are either ―0x or ―0X, + // then remove the first two characters from S and let R = 16. + // 11. If S contains any character that is not a radix-R digit, then let Z be the substring of S + // consisting of all characters before the first such character; otherwise, let Z be S. + 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) + radix = 10; + + // 8.a If R < 2 or R > 36, then return NaN. + if (radix < 2 || radix > 36) + return std::numeric_limits<double>::quiet_NaN(); + + // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters + // A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant + // digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation; + // and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the + // mathematical integer value that is represented by Z in radix-R notation.) + // 14. Let number be the Number value for mathInt. + 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); + } + + // 12. If Z is empty, return NaN. + if (!sawDigit) + return std::numeric_limits<double>::quiet_NaN(); + + // 15. Return sign x number. + return sign * number; +} + +static double parseInt(const UString& s, int radix) +{ + if (s.is8Bit()) + return parseInt(s, s.characters8(), radix); + return parseInt(s, s.characters16(), radix); +} + +static const int SizeOfInfinity = 8; + +template <typename CharType> +static bool isInfinity(const CharType* data, const CharType* 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 +template <typename CharType> +static double jsHexIntegerLiteral(const CharType*& data, const CharType* end) +{ + // Hex number. + data += 2; + const CharType* 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 +template <typename CharType> +static double jsStrDecimalLiteral(const CharType*& data, const CharType* end) +{ + ASSERT(data < end); + + // Copy the sting into a null-terminated byte buffer, and call strtod. + Vector<char, 32> byteBuffer; + for (const CharType* characters = data; characters < end; ++characters) { + CharType character = *characters; + byteBuffer.append(isASCII(character) ? static_cast<char>(character) : 0); + } + byteBuffer.append(0); + char* endOfNumber; + double number = WTF::strtod(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 std::numeric_limits<double>::infinity(); + } + break; + + case '+': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return std::numeric_limits<double>::infinity(); + } + break; + + case '-': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return -std::numeric_limits<double>::infinity(); + } + break; + } + + // Not a number. + return std::numeric_limits<double>::quiet_NaN(); +} + +template <typename CharType> +static double toDouble(const CharType* characters, unsigned size) +{ + const CharType* endCharacters = characters + size; + + // Skip leading white space. + for (; characters < endCharacters; ++characters) { + if (!isStrWhiteSpace(*characters)) + break; + } + + // Empty string. + if (characters == endCharacters) + return 0.0; + + double number; + if (characters[0] == '0' && characters + 2 < endCharacters && (characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2])) + number = jsHexIntegerLiteral(characters, endCharacters); + else + number = jsStrDecimalLiteral(characters, endCharacters); + + // Allow trailing white space. + for (; characters < endCharacters; ++characters) { + if (!isStrWhiteSpace(*characters)) + break; + } + if (characters != endCharacters) + return std::numeric_limits<double>::quiet_NaN(); + + return number; +} + +// See ecma-262 9.3.1 +double jsToNumber(const UString& s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s[0]; + if (isASCIIDigit(c)) + return c - '0'; + if (isStrWhiteSpace(c)) + return 0; + return std::numeric_limits<double>::quiet_NaN(); + } + + if (s.is8Bit()) + return toDouble(s.characters8(), size); + return toDouble(s.characters16(), size); +} + +static double parseFloat(const UString& s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s[0]; + if (isASCIIDigit(c)) + return c - '0'; + return std::numeric_limits<double>::quiet_NaN(); + } + + if (s.is8Bit()) { + const LChar* data = s.characters8(); + const LChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return std::numeric_limits<double>::quiet_NaN(); + + return jsStrDecimalLiteral(data, end); + } + + const UChar* data = s.characters16(); + const UChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return std::numeric_limits<double>::quiet_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); + + if (s.is8Bit()) { + LiteralParser<LChar> preparser(exec, s.characters8(), s.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return JSValue::encode(parsedObject); + } else { + LiteralParser<UChar> preparser(exec, s.characters16(), s.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return JSValue::encode(parsedObject); + } + + EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false); + JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain()); + if (error) + return throwVMError(exec, error); + + return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain())); +} + +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) +{ + JSValue value = exec->argument(0); + JSValue radixValue = exec->argument(1); + + // Optimized handling for numbers: + // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt + // results in a truncation to integer. In the case of -0, this is converted to 0. + // + // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21, + // however these values cannot be trivially truncated to int since 10^21 exceeds + // even the int64_t range. Negative numbers are a little trickier, the case for + // values in the range -10^21 < n <= -1 are similar to those for integer, but + // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0. + static const double tenToTheMinus6 = 0.000001; + static const double intMaxPlusOne = 2147483648.0; + if (value.isNumber()) { + double n = value.asNumber(); + if (((n < intMaxPlusOne && n >= tenToTheMinus6) || !n) && radixValue.isUndefinedOrNull()) + return JSValue::encode(jsNumber(static_cast<int32_t>(n))); + } + + // If ToString throws, we shouldn't call ToInt32. + UString s = value.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(jsNumber(parseInt(s, radixValue.toInt32(exec)))); +} + +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(isfinite(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); + if (str.is8Bit()) { + const LChar* c = str.characters8(); + for (unsigned k = 0; k < str.length(); k++, c++) { + int u = c[0]; + if (u && 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)); + } + + const UChar* c = str.characters16(); + 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(); + + if (str.is8Bit()) { + const LChar* characters = str.characters8(); + LChar convertedLChar; + while (k < len) { + const LChar* c = characters + k; + if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { + if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { + builder.append(Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5])); + k += 6; + continue; + } + } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { + convertedLChar = LChar(Lexer<LChar>::convertHex(c[1], c[2])); + c = &convertedLChar; + k += 2; + } + builder.append(*c); + k++; + } + } else { + const UChar* characters = str.characters16(); + + while (k < len) { + const UChar* c = characters + k; + UChar convertedUChar; + if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { + if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { + convertedUChar = Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]); + c = &convertedUChar; + k += 5; + } + } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { + convertedUChar = UChar(Lexer<UChar>::convertHex(c[1], c[2])); + c = &convertedUChar; + k += 2; + } + k++; + builder.append(*c); + } + } + + return JSValue::encode(jsString(exec, builder.toUString())); +} + +EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec) +{ + return throwVMTypeError(exec); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h new file mode 100644 index 000000000..1183dfac5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h @@ -0,0 +1,61 @@ +/* + * 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*); + EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState*); + + static const double mantissaOverflowLowerBound = 9007199254740992.0; + double parseIntOverflow(const LChar*, int length, int radix); + ALWAYS_INLINE double parseIntOverflow(const char* s, int length, int radix) { return parseIntOverflow(reinterpret_cast<const LChar*>(s), length, 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/JSGlobalThis.cpp b/Source/JavaScriptCore/runtime/JSGlobalThis.cpp new file mode 100644 index 000000000..8b2a7a1ef --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalThis.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011 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 "JSGlobalThis.h" + +#include "JSGlobalObject.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSGlobalThis); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSGlobalThis); + +const ClassInfo JSGlobalThis::s_info = { "JSGlobalThis", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSGlobalThis) }; + +void JSGlobalThis::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSGlobalThis* thisObject = jsCast<JSGlobalThis*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + + Base::visitChildren(thisObject, visitor); + if (thisObject->m_unwrappedObject) + visitor.append(&thisObject->m_unwrappedObject); +} + +JSGlobalObject* JSGlobalThis::unwrappedObject() +{ + return m_unwrappedObject.get(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalThis.h b/Source/JavaScriptCore/runtime/JSGlobalThis.h new file mode 100644 index 000000000..fa5de0491 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalThis.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 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 JSGlobalThis_h +#define JSGlobalThis_h + +#include "JSObject.h" + +namespace JSC { + +class JSGlobalThis : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static JSGlobalThis* create(JSGlobalData& globalData, Structure* structure) + { + JSGlobalThis* globalThis = new (NotNull, allocateCell<JSGlobalThis>(globalData.heap)) JSGlobalThis(globalData, structure); + globalThis->finishCreation(globalData); + return globalThis; + } + + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, 0, prototype, TypeInfo(GlobalThisType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const JSC::ClassInfo s_info; + + JSGlobalObject* unwrappedObject(); + +protected: + JSGlobalThis(JSGlobalData& globalData, Structure* structure) + : JSNonFinalObject(globalData, structure) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + } + + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; + + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSGlobalObject> m_unwrappedObject; +}; + +} // namespace JSC + +#endif // JSGlobalThis_h diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp new file mode 100644 index 000000000..90e2f5d2a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -0,0 +1,263 @@ +/* + * 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 "Heap.h" +#include "CallFrame.h" +#include "JSObject.h" +#include "ScopeChain.h" + +#if USE(PTHREADS) +#include <pthread.h> +#endif + +namespace JSC { + +// JSLock is only needed to support an obsolete execution model where JavaScriptCore +// automatically protected against concurrent access from multiple threads. +// So it's safe to disable it on non-mac platforms where we don't have native pthreads. +#if (OS(DARWIN) || USE(PTHREADS)) + +// 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 = pthread_mutex_lock(&JSMutex); + ASSERT_UNUSED(result, !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 = pthread_mutex_unlock(&JSMutex); + ASSERT_UNUSED(result, !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 // (OS(DARWIN) || USE(PTHREADS)) + +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 // (OS(DARWIN) || USE(PTHREADS)) + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSLock.h b/Source/JavaScriptCore/runtime/JSLock.h new file mode 100644 index 000000000..7b07b4fb3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.h @@ -0,0 +1,108 @@ +/* + * 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 { + WTF_MAKE_NONCOPYABLE(JSLock); + 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 { + WTF_MAKE_NONCOPYABLE(DropAllLocks); + 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 000000000..682400292 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -0,0 +1,95 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSNotAnObject); + +const ClassInfo JSNotAnObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSNotAnObject) }; + +// JSValue methods +JSValue JSNotAnObject::defaultValue(const JSObject*, ExecState* exec, PreferredPrimitiveType) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return jsNumber(0); +} + +// JSObject methods +bool JSNotAnObject::getOwnPropertySlot(JSCell*, ExecState* exec, const Identifier&, PropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::getOwnPropertySlotByIndex(JSCell*, ExecState* exec, unsigned, PropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::getOwnPropertyDescriptor(JSObject*, ExecState* exec, const Identifier&, PropertyDescriptor&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +void JSNotAnObject::put(JSCell*, ExecState* exec, const Identifier& , JSValue, PutPropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +void JSNotAnObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +bool JSNotAnObject::deleteProperty(JSCell*, ExecState* exec, const Identifier&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::deletePropertyByIndex(JSCell*, ExecState* exec, unsigned) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +void JSNotAnObject::getOwnPropertyNames(JSObject*, 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 000000000..51ba456e1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.h @@ -0,0 +1,86 @@ +/* + * 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 JSNonFinalObject { + private: + JSNotAnObject(ExecState* exec) + : JSNonFinalObject(exec->globalData(), exec->globalData().notAnObjectStructure.get()) + { + } + + public: + typedef JSNonFinalObject Base; + + static JSNotAnObject* create(ExecState* exec) + { + JSNotAnObject* object = new (NotNull, allocateCell<JSNotAnObject>(*exec->heap())) JSNotAnObject(exec); + object->finishCreation(exec->globalData()); + return object; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + private: + + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + + // JSValue methods + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + + // JSObject methods + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + }; + +} // namespace JSC + +#endif // JSNotAnObject_h diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp new file mode 100644 index 000000000..05c6c2953 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSONObject.cpp @@ -0,0 +1,879 @@ +/* + * 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 "Local.h" +#include "LocalScope.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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(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, Structure* structure) + : JSNonFinalObject(globalObject->globalData(), structure) +{ +} + +void JSONObject::finishCreation(JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalData()); + ASSERT(inherits(&s_info)); +} + +// 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 { + WTF_MAKE_NONCOPYABLE(Stringifier); +public: + Stringifier(ExecState*, const Local<Unknown>& replacer, const Local<Unknown>& space); + Local<Unknown> stringify(Handle<Unknown>); + + void visitAggregate(SlotVisitor&); + +private: + class Holder { + public: + Holder(JSGlobalData&, JSObject*); + + JSObject* object() const { return m_object.get(); } + + bool appendNextProperty(Stringifier&, UStringBuilder&); + + private: + Local<JSObject> 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; + + ExecState* const m_exec; + const Local<Unknown> m_replacer; + bool m_usingArrayReplacer; + PropertyNameArray m_arrayReplacerPropertyNames; + CallType m_replacerCallType; + CallData m_replacerCallData; + const UString m_gap; + + 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::s_info)) + return jsNumber(object->toNumber(exec)); + if (object->inherits(&StringObject::s_info)) + return jsString(exec, object->toString(exec)); + if (object->inherits(&BooleanObject::s_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. + if (space.isNumber()) { + double spaceCount = space.asNumber(); + 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, const Local<Unknown>& replacer, const Local<Unknown>& space) + : m_exec(exec) + , m_replacer(replacer) + , m_usingArrayReplacer(false) + , m_arrayReplacerPropertyNames(exec) + , m_replacerCallType(CallTypeNone) + , m_gap(gap(exec, space.get())) +{ + if (!m_replacer.isObject()) + return; + + if (m_replacer.asObject()->inherits(&JSArray::s_info)) { + m_usingArrayReplacer = true; + Handle<JSObject> array = m_replacer.asObject(); + 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; + } + + if (name.isNumber()) { + m_arrayReplacerPropertyNames.add(Identifier::from(exec, name.asNumber())); + continue; + } + + if (name.isObject()) { + if (!asObject(name)->inherits(&NumberObject::s_info) && !asObject(name)->inherits(&StringObject::s_info)) + continue; + propertyName = name.toString(exec); + if (exec->hadException()) + break; + m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); + } + } + return; + } + + m_replacerCallType = m_replacer.asObject()->methodTable()->getCallData(m_replacer.asObject().get(), m_replacerCallData); +} + +Local<Unknown> Stringifier::stringify(Handle<Unknown> value) +{ + JSObject* object = constructEmptyObject(m_exec); + if (m_exec->hadException()) + return Local<Unknown>(m_exec->globalData(), jsNull()); + + PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier); + object->putDirect(m_exec->globalData(), m_exec->globalData().propertyNames->emptyIdentifier, value.get()); + + UStringBuilder result; + if (appendStringifiedValue(result, value.get(), object, emptyPropertyName) != StringifySucceeded) + return Local<Unknown>(m_exec->globalData(), jsUndefined()); + if (m_exec->hadException()) + return Local<Unknown>(m_exec->globalData(), jsNull()); + + return Local<Unknown>(m_exec->globalData(), jsString(m_exec, result.toUString())); +} + +template <typename CharType> +static void appendStringToUStringBuilder(UStringBuilder& builder, const CharType* data, int length) +{ + 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]; + LChar 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; + } + } +} + +void Stringifier::appendQuotedString(UStringBuilder& builder, const UString& value) +{ + int length = value.length(); + + builder.append('"'); + + if (value.is8Bit()) + appendStringToUStringBuilder<LChar>(builder, value.characters8(), length); + else + appendStringToUStringBuilder<UChar>(builder, value.characters16(), length); + + 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->methodTable()->getCallData(object, callData); + if (callType == CallTypeNone) + return value; + + MarkedArgumentBuffer args; + args.append(propertyName.value(m_exec)); + 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) { + MarkedArgumentBuffer args; + args.append(propertyName.value(m_exec)); + args.append(value); + value = call(m_exec, m_replacer.get(), m_replacerCallType, m_replacerCallData, holder, args); + if (m_exec->hadException()) + return StringifyFailed; + } + + if (value.isUndefined() && !holder->inherits(&JSArray::s_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.isTrue() ? "true" : "false"); + return StringifySucceeded; + } + + UString stringValue; + if (value.getString(m_exec, stringValue)) { + appendQuotedString(builder, stringValue); + return StringifySucceeded; + } + + if (value.isNumber()) { + double number = value.asNumber(); + if (!isfinite(number)) + builder.append("null"); + else + builder.append(UString::number(number)); + return StringifySucceeded; + } + + if (!value.isObject()) + return StringifyFailed; + + JSObject* object = asObject(value); + + CallData callData; + if (object->methodTable()->getCallData(object, callData) != CallTypeNone) { + if (holder->inherits(&JSArray::s_info)) { + builder.append("null"); + return StringifySucceeded; + } + return StringifyFailedDueToUndefinedValue; + } + + // Handle cycle detection, and put the holder on the stack. + for (unsigned i = 0; i < m_holderStack.size(); i++) { + if (m_holderStack[i].object() == object) { + throwError(m_exec, createTypeError(m_exec, "JSON.stringify cannot serialize cyclic structures.")); + return StringifyFailed; + } + } + bool holderStackWasEmpty = m_holderStack.isEmpty(); + m_holderStack.append(Holder(m_exec->globalData(), 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_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(JSGlobalData& globalData, JSObject* object) + : m_object(globalData, object) + , m_isArray(object->inherits(&JSArray::s_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(m_object.get()); + 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->methodTable()->getOwnPropertyNames(m_object.get(), exec, objectPropertyNames, ExcludeDontEnumProperties); + 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.get())->canGetIndex(index)) + value = asArray(m_object.get())->getIndex(index); + else { + PropertySlot slot(m_object.get()); + if (!m_object->methodTable()->getOwnPropertySlotByIndex(m_object.get(), 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.get(), index); + } else { + // Get the value. + PropertySlot slot(m_object.get()); + Identifier& propertyName = m_propertyNames->propertyNameVector()[index]; + if (!m_object->methodTable()->getOwnPropertySlot(m_object.get(), 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.get(), 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::s_info = { "JSON", &JSNonFinalObject::s_info, 0, ExecState::jsonTable, CREATE_METHOD_TABLE(JSONObject) }; + +/* Source for JSONObject.lut.h +@begin jsonTable + parse JSONProtoFuncParse DontEnum|Function 2 + stringify JSONProtoFuncStringify DontEnum|Function 3 +@end +*/ + +// ECMA 15.8 + +bool JSONObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, ExecState::jsonTable(exec), jsCast<JSONObject*>(cell), propertyName, slot); +} + +bool JSONObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::jsonTable(exec), jsCast<JSONObject*>(object), propertyName, descriptor); +} + +class Walker { +public: + Walker(ExecState* exec, Handle<JSObject> function, CallType callType, CallData callData) + : m_exec(exec) + , m_function(exec->globalData(), function) + , m_callType(callType) + , m_callData(callData) + { + } + JSValue walk(JSValue unfiltered); +private: + JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered) + { + MarkedArgumentBuffer args; + args.append(property); + args.append(unfiltered); + return call(m_exec, m_function.get(), m_callType, m_callData, thisObj, args); + } + + friend class Holder; + + ExecState* m_exec; + Local<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; + LocalStack<JSObject, 16> objectStack(m_exec->globalData()); + LocalStack<JSArray, 16> arrayStack(m_exec->globalData()); + + 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(asObject(inValue)) || asObject(inValue)->inherits(&JSArray::s_info)); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwError(m_exec, createStackOverflowError(m_exec)); + + JSArray* array = asArray(inValue); + arrayStack.push(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.peek(); + uint32_t index = indexStack.last(); + if (index == array->length()) { + outValue = array; + arrayStack.pop(); + indexStack.removeLast(); + break; + } + if (isJSArray(array) && array->canGetIndex(index)) + inValue = array->getIndex(index); + else { + PropertySlot slot; + if (array->methodTable()->getOwnPropertySlotByIndex(array, 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.peek(); + JSValue filteredValue = callReviver(array, jsString(m_exec, UString::number(indexStack.last())), outValue); + if (filteredValue.isUndefined()) + array->methodTable()->deletePropertyByIndex(array, m_exec, indexStack.last()); + else { + if (isJSArray(array) && array->canSetIndex(indexStack.last())) + array->setIndex(m_exec->globalData(), indexStack.last(), filteredValue); + else + array->methodTable()->putByIndex(array, m_exec, indexStack.last(), filteredValue); + } + if (m_exec->hadException()) + return jsNull(); + indexStack.last()++; + goto arrayStartVisitMember; + } + objectStartState: + case ObjectStartState: { + ASSERT(inValue.isObject()); + ASSERT(!isJSArray(asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::s_info)); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwError(m_exec, createStackOverflowError(m_exec)); + + JSObject* object = asObject(inValue); + objectStack.push(object); + indexStack.append(0); + propertyStack.append(PropertyNameArray(m_exec)); + object->methodTable()->getOwnPropertyNames(object, m_exec, propertyStack.last(), ExcludeDontEnumProperties); + // 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.peek(); + uint32_t index = indexStack.last(); + PropertyNameArray& properties = propertyStack.last(); + if (index == properties.size()) { + outValue = object; + objectStack.pop(); + indexStack.removeLast(); + propertyStack.removeLast(); + break; + } + PropertySlot slot; + if (object->methodTable()->getOwnPropertySlot(object, 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.peek(); + Identifier prop = propertyStack.last()[indexStack.last()]; + PutPropertySlot slot; + JSValue filteredValue = callReviver(object, jsString(m_exec, prop.ustring()), outValue); + if (filteredValue.isUndefined()) + object->methodTable()->deleteProperty(object, m_exec, prop); + else + object->methodTable()->put(object, 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(object) || object->inherits(&JSArray::s_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->methodTable()->put(finalHolder, 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()); + + JSValue unfiltered; + LocalScope scope(exec->globalData()); + if (source.is8Bit()) { + LiteralParser<LChar> jsonParser(exec, source.characters8(), source.length(), StrictJSON); + unfiltered = jsonParser.tryLiteralParse(); + if (!unfiltered) + return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage())); + } else { + LiteralParser<UChar> jsonParser(exec, source.characters16(), source.length(), StrictJSON); + unfiltered = jsonParser.tryLiteralParse(); + if (!unfiltered) + return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage())); + } + + 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, Local<JSObject>(exec->globalData(), 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")); + LocalScope scope(exec->globalData()); + Local<Unknown> value(exec->globalData(), exec->argument(0)); + Local<Unknown> replacer(exec->globalData(), exec->argument(1)); + Local<Unknown> space(exec->globalData(), exec->argument(2)); + return JSValue::encode(Stringifier(exec, replacer, space).stringify(value).get()); +} + +UString JSONStringify(ExecState* exec, JSValue value, unsigned indent) +{ + LocalScope scope(exec->globalData()); + Local<Unknown> result = Stringifier(exec, Local<Unknown>(exec->globalData(), jsNull()), Local<Unknown>(exec->globalData(), jsNumber(indent))).stringify(Local<Unknown>(exec->globalData(), 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 000000000..29535f6d0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSONObject.h @@ -0,0 +1,68 @@ +/* + * 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 "JSObject.h" + +namespace JSC { + + class Stringifier; + + class JSONObject : public JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + static JSONObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + JSONObject* object = new (NotNull, allocateCell<JSONObject>(*exec->heap())) JSONObject(globalObject, structure); + object->finishCreation(globalObject); + return object; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + protected: + void finishCreation(JSGlobalObject*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObject::StructureFlags; + + private: + JSONObject(JSGlobalObject*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + }; + + 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 000000000..9cc29617a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -0,0 +1,848 @@ +/* + * 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 "JSGlobalThis.h" +#include "Lookup.h" +#include "NativeErrorConstructor.h" +#include "Nodes.h" +#include "ObjectPrototype.h" +#include "Operations.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.h" +#include <math.h> +#include <wtf/Assertions.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSObject); +ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); +ASSERT_CLASS_FITS_IN_CELL(JSFinalObject); + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject); + +const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; + +const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) }; + +const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) }; + +void JSFinalObject::destroy(JSCell* cell) +{ + jsCast<JSFinalObject*>(cell)->JSFinalObject::~JSFinalObject(); +} + +void JSNonFinalObject::destroy(JSCell* cell) +{ + jsCast<JSNonFinalObject*>(cell)->JSNonFinalObject::~JSNonFinalObject(); +} + +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::finalize(JSCell* cell) +{ + delete [] jsCast<JSObject*>(cell)->m_propertyStorage.get(); +} + +void JSObject::destroy(JSCell* cell) +{ + jsCast<JSObject*>(cell)->JSObject::~JSObject(); +} + +void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); +#if !ASSERT_DISABLED + bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; + visitor.m_isCheckingForDefaultMarkViolation = false; +#endif + + JSCell::visitChildren(thisObject, visitor); + + PropertyStorage storage = thisObject->propertyStorage(); + size_t storageSize = thisObject->structure()->propertyStorageSize(); + visitor.appendValues(storage, storageSize); + if (thisObject->m_inheritorID) + visitor.append(&thisObject->m_inheritorID); + +#if !ASSERT_DISABLED + visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; +#endif +} + +UString JSObject::className(const JSObject* object) +{ + const ClassInfo* info = object->classInfo(); + ASSERT(info); + return info->className; +} + +bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + return thisObject->methodTable()->getOwnPropertySlot(thisObject, 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(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + JSGlobalData& globalData = exec->globalData(); + + 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 (!thisObject->isExtensible()) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + if (!thisObject->setPrototypeWithCycleCheck(globalData, 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 = thisObject; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { + prototype = obj->prototype(); + if (prototype.isNull()) { + if (!thisObject->putDirectInternal(globalData, propertyName, value, 0, true, slot, getJSFunction(value)) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + } + + unsigned attributes; + JSCell* specificValue; + if ((thisObject->structure()->get(globalData, propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) { + if (slot.isStrictMode()) + throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + return; + } + + for (JSObject* obj = thisObject; ; obj = asObject(prototype)) { + if (JSValue gs = obj->getDirect(globalData, propertyName)) { + if (gs.isGetterSetter()) { + JSObject* setterFunc = asGetterSetter(gs)->setter(); + if (!setterFunc) { + throwSetterError(exec); + return; + } + + CallData callData; + CallType callType = setterFunc->methodTable()->getCallData(setterFunc, callData); + MarkedArgumentBuffer args; + args.append(value); + + // If this is WebCore's global object then we need to substitute the shell. + call(exec, setterFunc, callType, callData, thisObject->methodTable()->toThisObject(thisObject, exec), 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 (!thisObject->putDirectInternal(globalData, propertyName, value, 0, true, slot, getJSFunction(value)) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; +} + +void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value) +{ + PutPropertySlot slot; + JSObject* thisObject = jsCast<JSObject*>(cell); + thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); +} + +void JSObject::putWithAttributes(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot; + object->putDirectInternal(exec->globalData(), propertyName, value, attributes, true, slot, getJSFunction(value)); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot; + putDirectInternal(*globalData, propertyName, value, attributes, true, slot, getJSFunction(value)); +} + +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(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + + if (!thisObject->staticFunctionsReified()) + thisObject->reifyStaticFunctionsForDelete(exec); + + unsigned attributes; + JSCell* specificValue; + if (thisObject->structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) { + if ((attributes & DontDelete)) + return false; + thisObject->removeDirect(exec->globalData(), propertyName); + return true; + } + + // Look in the static hashtable of properties + const HashEntry* entry = thisObject->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)->methodTable()->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot); +} + +bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + return thisObject->methodTable()->deleteProperty(thisObject, 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) const +{ + result = methodTable()->defaultValue(this, exec, PreferNumber); + number = result.toNumber(exec); + return !result.isString(); +} + +// ECMA 8.6.2.6 +JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint) +{ + // Must call toString first for Date objects. + if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) { + JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString); + if (value) + return value; + value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf); + if (value) + return value; + } else { + JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf); + if (value) + return value; + value = callDefaultValueFunction(exec, object, 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(JSObject* thisObject, ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) +{ + if (propertyName == exec->propertyNames().underscoreProto) { + // Defining a getter for __proto__ is silently ignored. + return; + } + + JSValue object = thisObject->getDirect(exec->globalData(), propertyName); + if (object && object.isGetterSetter()) { + ASSERT(thisObject->structure()->hasGetterSetterProperties()); + asGetterSetter(object)->setGetter(exec->globalData(), getterFunction); + return; + } + + JSGlobalData& globalData = exec->globalData(); + PutPropertySlot slot; + GetterSetter* getterSetter = GetterSetter::create(exec); + thisObject->putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot, 0); + + // 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 (!thisObject->structure()->isDictionary()) + thisObject->setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, thisObject->structure())); + } + + thisObject->structure()->setHasGetterSetterProperties(true); + getterSetter->setGetter(globalData, getterFunction); +} + +void JSObject::initializeGetterSetterProperty(ExecState* exec, const Identifier& propertyName, GetterSetter* getterSetter, unsigned attributes) +{ + // Set an inital property on an object; the property must not already exist & the attribute flags must be set correctly. + ASSERT(structure()->get(exec->globalData(), propertyName) == WTF::notFound); + ASSERT(static_cast<bool>(getterSetter->getter()) == static_cast<bool>(attributes & Getter)); + ASSERT(static_cast<bool>(getterSetter->setter()) == static_cast<bool>(attributes & Setter)); + + JSGlobalData& globalData = exec->globalData(); + PutPropertySlot slot; + putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot, 0); + + // 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 (!structure()->isDictionary()) + setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, structure())); + } + + structure()->setHasGetterSetterProperties(true); +} + +void JSObject::defineSetter(JSObject* thisObject, ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) +{ + if (propertyName == exec->propertyNames().underscoreProto) { + // Defining a setter for __proto__ is silently ignored. + return; + } + + JSValue object = thisObject->getDirect(exec->globalData(), propertyName); + if (object && object.isGetterSetter()) { + ASSERT(thisObject->structure()->hasGetterSetterProperties()); + asGetterSetter(object)->setSetter(exec->globalData(), setterFunction); + return; + } + + PutPropertySlot slot; + GetterSetter* getterSetter = GetterSetter::create(exec); + thisObject->putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot, 0); + + // 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 (!thisObject->structure()->isDictionary()) + thisObject->setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), thisObject->structure())); + } + + thisObject->structure()->setHasGetterSetterProperties(true); + getterSetter->setSetter(exec->globalData(), setterFunction); +} + +JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName) +{ + PropertyDescriptor descriptor; + if (!getPropertyDescriptor(exec, propertyName, descriptor)) + return jsUndefined(); + + if (!descriptor.getterPresent()) + return jsUndefined(); + + return descriptor.getter(); +} + +JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName) +{ + PropertyDescriptor descriptor; + if (!getPropertyDescriptor(exec, propertyName, descriptor)) + return jsUndefined(); + + if (!descriptor.setterPresent()) + return jsUndefined(); + + return descriptor.setter(); +} + +bool JSObject::hasInstance(JSObject*, 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)->methodTable()->getOwnPropertyDescriptor(const_cast<JSObject*>(this), exec, propertyName, descriptor)) + return false; + return descriptor.enumerable(); +} + +bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const +{ + unsigned attributes; + if (structure()->get(exec->globalData(), 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(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + object->methodTable()->getOwnPropertyNames(object, exec, propertyNames, mode); + + if (object->prototype().isNull()) + return; + + JSObject* prototype = asObject(object->prototype()); + while(1) { + if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { + prototype->methodTable()->getPropertyNames(prototype, exec, propertyNames, mode); + break; + } + prototype->methodTable()->getOwnPropertyNames(prototype, exec, propertyNames, mode); + JSValue nextProto = prototype->prototype(); + if (nextProto.isNull()) + break; + prototype = asObject(nextProto); + } +} + +void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode); + if (!object->staticFunctionsReified()) + getClassPropertyNames(exec, object->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::toThisObject(JSCell* cell, ExecState*) +{ + return static_cast<JSObject*>(cell); +} + +JSObject* JSObject::unwrappedObject() +{ + if (isGlobalThis()) + return static_cast<JSGlobalThis*>(this)->unwrappedObject(); + return this; +} + +void JSObject::seal(JSGlobalData& globalData) +{ + if (isSealed(globalData)) + return; + preventExtensions(globalData); + setStructure(globalData, Structure::sealTransition(globalData, structure())); +} + +void JSObject::freeze(JSGlobalData& globalData) +{ + if (isFrozen(globalData)) + return; + preventExtensions(globalData); + setStructure(globalData, Structure::freezeTransition(globalData, structure())); +} + +void JSObject::preventExtensions(JSGlobalData& globalData) +{ + if (isExtensible()) + setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure())); +} + +// This presently will flatten to an uncachable dictionary; this is suitable +// for use in delete, we may want to do something different elsewhere. +void JSObject::reifyStaticFunctionsForDelete(ExecState* exec) +{ + ASSERT(!staticFunctionsReified()); + JSGlobalData& globalData = exec->globalData(); + + // If this object's ClassInfo has no static properties, then nothing to reify! + // We can safely set the flag to avoid the expensive check again in the future. + if (!classInfo()->hasStaticProperties()) { + structure()->setStaticFunctionsReified(); + return; + } + + if (!structure()->isUncacheableDictionary()) + setStructure(globalData, Structure::toUncacheableDictionaryTransition(globalData, structure())); + + for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { + const HashTable* hashTable = info->propHashTable(globalObject()->globalExec()); + if (!hashTable) + continue; + PropertySlot slot; + for (HashTable::ConstIterator iter = hashTable->begin(globalData); iter != hashTable->end(globalData); ++iter) { + if (iter->attributes() & Function) + setUpStaticFunctionSlot(globalObject()->globalExec(), *iter, this, Identifier(&globalData, iter->key()), slot); + } + } + + structure()->setStaticFunctionsReified(); +} + +void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName) +{ + if (structure()->get(globalData, propertyName) == WTF::notFound) + return; + + size_t offset; + if (structure()->isUncacheableDictionary()) { + offset = structure()->removePropertyWithoutTransition(globalData, propertyName); + if (offset != WTF::notFound) + putUndefinedAtDirectOffset(offset); + return; + } + + setStructure(globalData, Structure::removePropertyTransition(globalData, structure(), propertyName, offset)); + if (offset != WTF::notFound) + putUndefinedAtDirectOffset(offset); +} + +NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location) +{ + if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) { + if (!structure()->isDictionary()) + slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); + else + slot.setGetterSlot(getterFunction); + } else + slot.setUndefined(); +} + +Structure* JSObject::createInheritorID(JSGlobalData& globalData) +{ + m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, structure()->globalObject(), this)); + ASSERT(m_inheritorID->isEmpty()); + return m_inheritorID.get(); +} + +void JSObject::allocatePropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) +{ + ASSERT(newSize > oldSize); + + // It's important that this function not rely on structure(), since + // we might be in the middle of a transition. + PropertyStorage newPropertyStorage = 0; + newPropertyStorage = new WriteBarrierBase<Unknown>[newSize]; + + PropertyStorage oldPropertyStorage = m_propertyStorage.get(); + ASSERT(newPropertyStorage); + + for (unsigned i = 0; i < oldSize; ++i) + newPropertyStorage[i] = oldPropertyStorage[i]; + + if (isUsingInlineStorage()) + Heap::heap(this)->addFinalizer(this, &finalize); + else + delete [] oldPropertyStorage; + + m_propertyStorage.set(globalData, this, newPropertyStorage); +} + +bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + unsigned attributes = 0; + JSCell* cell = 0; + size_t offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell); + if (offset == WTF::notFound) + return false; + descriptor.setDescriptor(object->getDirectOffset(offset), attributes); + return true; +} + +bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSObject* object = this; + while (true) { + if (object->methodTable()->getOwnPropertyDescriptor(object, 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, const PropertyDescriptor& oldDescriptor) +{ + if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { + if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { + GetterSetter* accessor = GetterSetter::create(exec); + if (oldDescriptor.getter()) { + attributes |= Getter; + accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter())); + } + if (oldDescriptor.setter()) { + attributes |= Setter; + accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter())); + } + target->methodTable()->putWithAttributes(target, exec, propertyName, accessor, attributes); + return true; + } + JSValue newValue = jsUndefined(); + if (descriptor.value()) + newValue = descriptor.value(); + else if (oldDescriptor.value()) + newValue = oldDescriptor.value(); + target->methodTable()->putWithAttributes(target, exec, propertyName, newValue, attributes & ~(Getter | Setter)); + return true; + } + attributes &= ~ReadOnly; + if (descriptor.getter() && descriptor.getter().isObject()) + target->methodTable()->defineGetter(target, exec, propertyName, asObject(descriptor.getter()), attributes); + if (exec->hadException()) + return false; + if (descriptor.setter() && descriptor.setter().isObject()) + target->methodTable()->defineSetter(target, exec, propertyName, asObject(descriptor.setter()), attributes); + return !exec->hadException(); +} + +bool JSObject::defineOwnProperty(JSObject* object, 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 (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) { + // unless extensions are prevented! + if (!object->isExtensible()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible.")); + return false; + } + PropertyDescriptor oldDescriptor; + oldDescriptor.setValue(jsUndefined()); + return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor); + } + + 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)) { + object->methodTable()->deleteProperty(object, exec, propertyName); + putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current); + } + 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; + } + object->methodTable()->deleteProperty(object, exec, propertyName); + return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current); + } + + // 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; + object->methodTable()->put(object, exec, propertyName, descriptor.value(), slot); + if (exec->hadException()) + return false; + return true; + } + object->methodTable()->deleteProperty(object, exec, propertyName); + return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current); + } + + // Changing the accessor functions of an existing accessor property + ASSERT(descriptor.isAccessorDescriptor()); + if (!current.configurable()) { + if (descriptor.setterPresent() && !(current.setterPresent() && 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.getterPresent() && 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 = object->getDirect(exec->globalData(), propertyName); + if (!accessor) + return false; + GetterSetter* getterSetter = asGetterSetter(accessor); + if (current.attributesEqual(descriptor)) { + if (descriptor.setter()) + getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter())); + if (descriptor.getter()) + getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter())); + return true; + } + object->methodTable()->deleteProperty(object, exec, propertyName); + unsigned attrs = current.attributesWithOverride(descriptor); + if (descriptor.setter()) + attrs |= Setter; + if (descriptor.getter()) + attrs |= Getter; + object->putDirect(exec->globalData(), 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 000000000..e26012dd9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -0,0 +1,879 @@ +/* + * 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 "CallFrame.h" +#include "JSCell.h" +#include "PropertySlot.h" +#include "PutPropertySlot.h" +#include "ScopeChain.h" +#include "StorageBarrier.h" +#include "Structure.h" +#include "JSGlobalData.h" +#include "JSString.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + + inline JSCell* getJSFunction(JSValue value) + { + if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType)) + return value.asCell(); + return 0; + } + + class GetterSetter; + class HashEntry; + class InternalFunction; + class MarkedBlock; + class PropertyDescriptor; + class PropertyNameArray; + class Structure; + struct HashTable; + + JSObject* throwTypeError(ExecState*, const UString&); + extern JS_EXPORTDATA 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 + }; + + class JSObject : public JSCell { + friend class BatchedTransitionOptimizer; + friend class JIT; + friend class JSCell; + friend class MarkedBlock; + friend bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); + + public: + typedef JSCell Base; + + static void destroy(JSCell*); + + static void visitChildren(JSCell*, SlotVisitor&); + + static UString className(const JSObject*); + + static void finalize(JSCell*); + + JSValue prototype() const; + void setPrototype(JSGlobalData&, JSValue prototype); + bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype); + + Structure* inheritorID(JSGlobalData&); + + 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&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue); + + static void putWithAttributes(JSObject*, ExecState*, const Identifier& propertyName, JSValue, unsigned attributes); + void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue, 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; + + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + + static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty); + + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; + bool toBoolean(ExecState*) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + double toNumber(ExecState*) const; + UString toString(ExecState*) const; + + // NOTE: JSObject and its subclasses must be able to gracefully handle ExecState* = 0, + // because this call may come from inside the compiler. + static JSObject* toThisObject(JSCell*, ExecState*); + JSObject* unwrappedObject(); + + bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; + + // This get function only looks at the property map. + JSValue getDirect(JSGlobalData& globalData, const Identifier& propertyName) const + { + size_t offset = structure()->get(globalData, propertyName); + return offset != WTF::notFound ? getDirectOffset(offset) : JSValue(); + } + + WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName) + { + size_t offset = structure()->get(globalData, propertyName); + return offset != WTF::notFound ? locationForOffset(offset) : 0; + } + + WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes) + { + JSCell* specificFunction; + size_t offset = structure()->get(globalData, propertyName, attributes, specificFunction); + return offset != WTF::notFound ? locationForOffset(offset) : 0; + } + + size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const + { + return location - propertyStorage(); + } + + void transitionTo(JSGlobalData&, Structure*); + + void removeDirect(JSGlobalData&, const Identifier& propertyName); + bool hasCustomProperties() { return structure()->didTransition(); } + bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); } + + bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&); + void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0); + bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&); + + void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0); + + // Fast access to known property offsets. + JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); } + void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); } + void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); } + + void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location); + void initializeGetterSetterProperty(ExecState*, const Identifier&, GetterSetter*, unsigned attributes); + + static void defineGetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0); + static void defineSetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); + JSValue lookupGetter(ExecState*, const Identifier& propertyName); + JSValue lookupSetter(ExecState*, const Identifier& propertyName); + static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); + + bool isGlobalObject() const; + bool isVariableObject() const; + bool isActivationObject() const; + bool isErrorInstance() const; + bool isGlobalThis() const; + + void seal(JSGlobalData&); + void freeze(JSGlobalData&); + void preventExtensions(JSGlobalData&); + bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); } + bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); } + bool isExtensible() { return structure()->isExtensible(); } + + bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } + void reifyStaticFunctionsForDelete(ExecState* exec); + + void allocatePropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize); + bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1); } + + void* addressOfPropertyStorage() + { + return &m_propertyStorage; + } + + static const unsigned baseExternalStorageCapacity = 16; + + void flattenDictionaryObject(JSGlobalData& globalData) + { + structure()->flattenDictionaryStructure(globalData, this); + } + + JSGlobalObject* globalObject() const + { + ASSERT(structure()->globalObject()); + ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); + return structure()->globalObject(); + } + + static size_t offsetOfInlineStorage(); + static size_t offsetOfPropertyStorage(); + static size_t offsetOfInheritorID(); + + static JS_EXPORTDATA const ClassInfo s_info; + + protected: + void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage) + { + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity); + ASSERT(structure()->isEmpty()); + ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); + ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1)); + ASSERT(structure()->isObject()); + ASSERT(classInfo()); + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static const unsigned StructureFlags = 0; + + // To instantiate objects you likely want JSFinalObject, below. + // To create derived types you likely want JSNonFinalObject, below. + JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage); + + private: + // Nobody should ever ask any of these questions on something already known to be a JSObject. + using JSCell::isAPIValueWrapper; + using JSCell::isGetterSetter; + void getObject(); + void getString(ExecState* exec); + void isObject(); + void isString(); + + ConstPropertyStorage propertyStorage() const { return m_propertyStorage.get(); } + PropertyStorage propertyStorage() { return m_propertyStorage.get(); } + + const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const + { + return &propertyStorage()[offset]; + } + + WriteBarrierBase<Unknown>* locationForOffset(size_t offset) + { + return &propertyStorage()[offset]; + } + + bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*); + + bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + + const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; + Structure* createInheritorID(JSGlobalData&); + + StorageBarrier m_propertyStorage; + WriteBarrier<Structure> m_inheritorID; + }; + + +#if USE(JSVALUE32_64) +#define JSNonFinalObject_inlineStorageCapacity 4 +#define JSFinalObject_inlineStorageCapacity 6 +#else +#define JSNonFinalObject_inlineStorageCapacity 2 +#define JSFinalObject_inlineStorageCapacity 4 +#endif + +COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final); + + // JSNonFinalObject is a type of JSObject that has some internal storage, + // but also preserves some space in the collector cell for additional + // data members in derived types. + class JSNonFinalObject : public JSObject { + friend class JSObject; + + public: + typedef JSObject Base; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static void destroy(JSCell*); + + protected: + explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure) + : JSObject(globalData, structure, m_inlineStorage) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData, m_inlineStorage); + ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double))); + ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity); + ASSERT(classInfo()); + } + + private: + WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity]; + }; + + // JSFinalObject is a type of JSObject that contains sufficent internal + // storage to fully make use of the colloctor cell containing it. + class JSFinalObject : public JSObject { + friend class JSObject; + + public: + typedef JSObject Base; + + static JSFinalObject* create(ExecState* exec, Structure* structure) + { + JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure); + finalObject->finishCreation(exec->globalData()); + return finalObject; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; + + protected: + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData, m_inlineStorage); + ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double))); + ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity); + ASSERT(classInfo()); + } + + static void destroy(JSCell*); + + private: + explicit JSFinalObject(JSGlobalData& globalData, Structure* structure) + : JSObject(globalData, structure, m_inlineStorage) + { + } + + static const unsigned StructureFlags = JSObject::StructureFlags; + + WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity]; + }; + +inline bool isJSFinalObject(JSCell* cell) +{ + return cell->classInfo() == &JSFinalObject::s_info; +} + +inline bool isJSFinalObject(JSValue value) +{ + return value.isCell() && isJSFinalObject(value.asCell()); +} + +inline size_t JSObject::offsetOfInlineStorage() +{ + ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage)); + return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage); +} + +inline size_t JSObject::offsetOfPropertyStorage() +{ + return OBJECT_OFFSETOF(JSObject, m_propertyStorage); +} + +inline size_t JSObject::offsetOfInheritorID() +{ + return OBJECT_OFFSETOF(JSObject, m_inheritorID); +} + +inline bool JSObject::isGlobalObject() const +{ + return structure()->typeInfo().type() == GlobalObjectType; +} + +inline bool JSObject::isVariableObject() const +{ + return structure()->typeInfo().type() >= VariableObjectType; +} + +inline bool JSObject::isActivationObject() const +{ + return structure()->typeInfo().type() == ActivationObjectType; +} + +inline bool JSObject::isErrorInstance() const +{ + return structure()->typeInfo().type() == ErrorInstanceType; +} + +inline bool JSObject::isGlobalThis() const +{ + return structure()->typeInfo().type() == GlobalThisType; +} + +inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure) +{ + return JSFinalObject::create(exec, structure); +} + +inline CallType getCallData(JSValue value, CallData& callData) +{ + CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone; + ASSERT(result == CallTypeNone || value.isValidCallee()); + return result; +} + +inline ConstructType getConstructData(JSValue value, ConstructData& constructData) +{ + ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone; + ASSERT(result == ConstructTypeNone || value.isValidCallee()); + return result; +} + +inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) +{ + return JSFinalObject::createStructure(globalData, globalObject, prototype); +} + +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(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage) + : JSCell(globalData, structure) + , m_propertyStorage(globalData, this, inlineStorage) +{ +} + +inline JSValue JSObject::prototype() const +{ + return structure()->storedPrototype(); +} + +inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype) +{ + JSValue nextPrototypeValue = prototype; + while (nextPrototypeValue && nextPrototypeValue.isObject()) { + JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); + if (nextPrototype == this) + return false; + nextPrototypeValue = nextPrototype->prototype(); + } + setPrototype(globalData, prototype); + return true; +} + +inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) +{ + ASSERT(prototype); + setStructure(globalData, Structure::changePrototypeTransition(globalData, structure(), prototype)); +} + +inline Structure* JSObject::inheritorID(JSGlobalData& globalData) +{ + if (m_inheritorID) { + ASSERT(m_inheritorID->isEmpty()); + return m_inheritorID.get(); + } + return createInheritorID(globalData); +} + +inline bool Structure::isUsingInlineStorage() const +{ + return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity; +} + +inline bool JSCell::inherits(const ClassInfo* info) const +{ + return classInfo()->isSubClassOf(info); +} + +inline const MethodTable* JSCell::methodTable() const +{ + return &classInfo()->methodTable; +} + +// 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); +} + +inline JSObject* JSValue::toThisObject(ExecState* exec) const +{ + return isCell() ? asCell()->methodTable()->toThisObject(asCell(), exec) : toThisObjectSlowCase(exec); +} + +ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) { + if (structure()->hasGetterSetterProperties() && location->isGetterSetter()) + fillGetterPropertySlot(slot, location); + else + slot.setValue(this, location->get(), 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(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return jsCast<JSObject*>(cell)->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 methodTable()->getOwnPropertySlot(this, exec, propertyName, slot); +} + +// Fast call to get a property where we may not yet have converted the string to an +// identifier. The first time we perform a property access with a given string, try +// performing the property map lookup without forming an identifier. We detect this +// case by checking whether the hash has yet been set for this string. +ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name) +{ + if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) { + size_t offset = name.impl()->hasHash() + ? structure()->get(exec->globalData(), Identifier(exec, name)) + : structure()->get(exec->globalData(), name); + if (offset != WTF::notFound) + return asObject(this)->locationForOffset(offset)->get(); + } + return JSValue(); +} + +// 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->methodTable()->getOwnPropertySlotByIndex(object, 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(JSGlobalData& globalData, 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 (structure()->isDictionary()) { + unsigned currentAttributes; + JSCell* currentSpecificFunction; + size_t offset = structure()->get(globalData, 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)) + structure()->despecifyDictionaryFunction(globalData, propertyName); + if (checkReadOnly && currentAttributes & ReadOnly) + return false; + + putDirectOffset(globalData, 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; + } + + if (checkReadOnly && !isExtensible()) + return false; + + size_t currentCapacity = structure()->propertyStorageCapacity(); + offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction); + if (currentCapacity != structure()->propertyStorageCapacity()) + allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity()); + + ASSERT(offset < structure()->propertyStorageCapacity()); + putDirectOffset(globalData, offset, value); + // See comment on setNewProperty call below. + if (!specificFunction) + slot.setNewProperty(this, offset); + return true; + } + + size_t offset; + size_t currentCapacity = structure()->propertyStorageCapacity(); + if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { + if (currentCapacity != structure->propertyStorageCapacity()) + allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity()); + + ASSERT(offset < structure->propertyStorageCapacity()); + setStructure(globalData, structure); + putDirectOffset(globalData, offset, value); + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. + if (!specificFunction) + slot.setNewProperty(this, offset); + return true; + } + + unsigned currentAttributes; + JSCell* currentSpecificFunction; + offset = structure()->get(globalData, 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(globalData, offset, value); + return true; + } + // case (2) Despecify, fall through to (3). + setStructure(globalData, Structure::despecifyFunctionTransition(globalData, structure(), propertyName)); + } + + // case (3) set the slot, do the put, return. + slot.setExistingProperty(this, offset); + putDirectOffset(globalData, offset, value); + return true; + } + + if (checkReadOnly && !isExtensible()) + return false; + + Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset); + + if (currentCapacity != structure->propertyStorageCapacity()) + allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity()); + + ASSERT(offset < structure->propertyStorageCapacity()); + setStructure(globalData, structure); + putDirectOffset(globalData, offset, value); + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. + if (!specificFunction) + slot.setNewProperty(this, offset); + return true; +} + +inline bool JSObject::putDirect(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(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(value)); +} + +inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot; + putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(value)); +} + +inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + return putDirectInternal(globalData, propertyName, value, 0, false, slot, getJSFunction(value)); +} + +inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + size_t currentCapacity = structure()->propertyStorageCapacity(); + size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getJSFunction(value)); + if (currentCapacity != structure()->propertyStorageCapacity()) + allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity()); + putDirectOffset(globalData, offset, value); +} + +inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure) +{ + if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) + allocatePropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); + setStructure(globalData, newStructure); +} + +inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +{ + return methodTable()->defaultValue(this, 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->methodTable()->getOwnPropertySlotByIndex(cell, 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())) { + JSObject* thisObject = synthesizeObject(exec); + thisObject->methodTable()->put(thisObject, exec, propertyName, value, slot); + return; + } + asCell()->methodTable()->put(asCell(), exec, propertyName, value, slot); +} + +inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(isCell() && isObject()); + if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + +inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) +{ + if (UNLIKELY(!isCell())) { + JSObject* thisObject = synthesizeObject(exec); + thisObject->methodTable()->putByIndex(thisObject, exec, propertyName, value); + return; + } + asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value); +} + +// --- JSValue inlines ---------------------------- + +ALWAYS_INLINE JSObject* Register::function() const +{ + if (!jsValue()) + return 0; + return asObject(jsValue()); +} + +ALWAYS_INLINE Register Register::withCallee(JSObject* callee) +{ + Register r; + r = JSValue(callee); + return r; +} + +} // namespace JSC + +#endif // JSObject_h diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp new file mode 100644 index 000000000..6fd8b770b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -0,0 +1,110 @@ +/* + * 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); + +const ClassInfo JSPropertyNameIterator::s_info = { "JSPropertyNameIterator", 0, 0, 0, CREATE_METHOD_TABLE(JSPropertyNameIterator) }; + +inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlots) + : JSCell(exec->globalData(), exec->globalData().propertyNameIteratorStructure.get()) + , m_numCacheableSlots(numCacheableSlots) + , m_jsStringsSize(propertyNameArrayData->propertyNameVector().size()) + , m_jsStrings(adoptArrayPtr(new WriteBarrier<Unknown>[m_jsStringsSize])) +{ +} + +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->methodTable()->getPropertyNames(o, exec, propertyNames, ExcludeDontEnumProperties); + size_t numCacheableSlots = 0; + if (!o->structure()->hasNonEnumerableProperties() && !o->structure()->hasGetterSetterProperties() + && !o->structure()->isUncacheableDictionary() && !o->structure()->typeInfo().overridesGetPropertyNames()) + numCacheableSlots = o->structure()->propertyStorageSize(); + + JSPropertyNameIterator* jsPropertyNameIterator = new (NotNull, allocateCell<JSPropertyNameIterator>(*exec->heap())) JSPropertyNameIterator(exec, propertyNames.data(), numCacheableSlots); + jsPropertyNameIterator->finishCreation(exec, propertyNames.data()); + + 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); + WriteBarrier<Structure>* structure = structureChain->head(); + for (size_t i = 0; i < count; ++i) { + if (structure[i]->typeInfo().overridesGetPropertyNames()) + return jsPropertyNameIterator; + } + + jsPropertyNameIterator->setCachedPrototypeChain(exec->globalData(), structureChain); + jsPropertyNameIterator->setCachedStructure(exec->globalData(), o->structure()); + o->structure()->setEnumerationCache(exec->globalData(), jsPropertyNameIterator); + return jsPropertyNameIterator; +} + +void JSPropertyNameIterator::destroy(JSCell* cell) +{ + jsCast<JSPropertyNameIterator*>(cell)->JSPropertyNameIterator::~JSPropertyNameIterator(); +} + +JSValue JSPropertyNameIterator::get(ExecState* exec, JSObject* base, size_t i) +{ + JSValue identifier = m_jsStrings[i].get(); + if (m_cachedStructure.get() == base->structure() && m_cachedPrototypeChain.get() == base->structure()->prototypeChain(exec)) + return identifier; + + if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value(exec)))) + return JSValue(); + return identifier; +} + +void JSPropertyNameIterator::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSPropertyNameIterator* thisObject = jsCast<JSPropertyNameIterator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + visitor.appendValues(thisObject->m_jsStrings.get(), thisObject->m_jsStringsSize); + if (thisObject->m_cachedPrototypeChain) + visitor.append(&thisObject->m_cachedPrototypeChain); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h new file mode 100644 index 000000000..d52e3ea61 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -0,0 +1,126 @@ +/* + * 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: + typedef JSCell Base; + + static JSPropertyNameIterator* create(ExecState*, JSObject*); + static JSPropertyNameIterator* create(ExecState* exec, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlot) + { + JSPropertyNameIterator* iterator = new (NotNull, allocateCell<JSPropertyNameIterator>(*exec->heap())) JSPropertyNameIterator(exec, propertyNameArrayData, numCacheableSlot); + iterator->finishCreation(exec, propertyNameArrayData); + return iterator; + } + + static void destroy(JSCell*); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(CompoundType, OverridesVisitChildren), &s_info); + } + + static void visitChildren(JSCell*, SlotVisitor&); + + 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(JSGlobalData& globalData, Structure* structure) + { + ASSERT(!m_cachedStructure); + ASSERT(structure); + m_cachedStructure.set(globalData, this, structure); + } + Structure* cachedStructure() { return m_cachedStructure.get(); } + + void setCachedPrototypeChain(JSGlobalData& globalData, StructureChain* cachedPrototypeChain) { m_cachedPrototypeChain.set(globalData, this, cachedPrototypeChain); } + StructureChain* cachedPrototypeChain() { return m_cachedPrototypeChain.get(); } + + static const ClassInfo s_info; + + protected: + void finishCreation(ExecState* exec, PropertyNameArrayData* propertyNameArrayData) + { + Base::finishCreation(exec->globalData()); + PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArrayData->propertyNameVector(); + for (size_t i = 0; i < m_jsStringsSize; ++i) + m_jsStrings[i].set(exec->globalData(), this, jsOwnedString(exec, propertyNameVector[i].ustring())); + } + + private: + JSPropertyNameIterator(ExecState*, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlot); + + WriteBarrier<Structure> m_cachedStructure; + WriteBarrier<StructureChain> m_cachedPrototypeChain; + uint32_t m_numCacheableSlots; + uint32_t m_jsStringsSize; + OwnArrayPtr<WriteBarrier<Unknown> > m_jsStrings; + }; + + inline void Structure::setEnumerationCache(JSGlobalData& globalData, JSPropertyNameIterator* enumerationCache) + { + ASSERT(!isDictionary()); + m_enumerationCache.set(globalData, this, enumerationCache); + } + + inline JSPropertyNameIterator* Structure::enumerationCache() + { + return m_enumerationCache.get(); + } + + ALWAYS_INLINE JSPropertyNameIterator* Register::propertyNameIterator() const + { + return static_cast<JSPropertyNameIterator*>(jsValue().asCell()); + } + +} // namespace JSC + +#endif // JSPropertyNameIterator_h diff --git a/Source/JavaScriptCore/runtime/JSStaticScopeObject.cpp b/Source/JavaScriptCore/runtime/JSStaticScopeObject.cpp new file mode 100644 index 000000000..ada921d0e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStaticScopeObject.cpp @@ -0,0 +1,95 @@ +/* + * 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" + +#include "Error.h" + +namespace JSC { +ASSERT_CLASS_FITS_IN_CELL(JSStaticScopeObject); + +const ClassInfo JSStaticScopeObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSStaticScopeObject) }; + +void JSStaticScopeObject::destroy(JSCell* cell) +{ + jsCast<JSStaticScopeObject*>(cell)->JSStaticScopeObject::~JSStaticScopeObject(); +} + +void JSStaticScopeObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSStaticScopeObject* thisObject = jsCast<JSStaticScopeObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + JSVariableObject::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_registerStore); +} + +JSObject* JSStaticScopeObject::toThisObject(JSCell*, ExecState* exec) +{ + return exec->globalThisValue(); +} + +void JSStaticScopeObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + JSStaticScopeObject* thisObject = jsCast<JSStaticScopeObject*>(cell); + if (slot.isStrictMode()) { + // Double lookup in strict mode, but this only occurs when + // a) indirectly writing to an exception slot + // b) writing to a function expression name + // (a) is unlikely, and (b) is an error. + // Also with a single entry the symbol table lookup should simply be + // a pointer compare. + PropertySlot slot; + bool isWritable = true; + thisObject->symbolTableGet(propertyName, slot, isWritable); + if (!isWritable) { + throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + return; + } + } + if (thisObject->symbolTablePut(exec, propertyName, value, slot.isStrictMode())) + return; + + ASSERT_NOT_REACHED(); +} + +void JSStaticScopeObject::putWithAttributes(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + JSStaticScopeObject* thisObject = jsCast<JSStaticScopeObject*>(object); + if (thisObject->symbolTablePutWithAttributes(exec->globalData(), propertyName, value, attributes)) + return; + + ASSERT_NOT_REACHED(); +} + +bool JSStaticScopeObject::getOwnPropertySlot(JSCell* cell, ExecState*, const Identifier& propertyName, PropertySlot& slot) +{ + return jsCast<JSStaticScopeObject*>(cell)->symbolTableGet(propertyName, slot); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSStaticScopeObject.h b/Source/JavaScriptCore/runtime/JSStaticScopeObject.h new file mode 100644 index 000000000..6a2e51a15 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStaticScopeObject.h @@ -0,0 +1,84 @@ +/* + * 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 { + public: + typedef JSVariableObject Base; + + static JSStaticScopeObject* create(ExecState* exec, const Identifier& identifier, JSValue value, unsigned attributes) + { + JSStaticScopeObject* scopeObject = new (NotNull, allocateCell<JSStaticScopeObject>(*exec->heap())) JSStaticScopeObject(exec); + scopeObject->finishCreation(exec, identifier, value, attributes); + return scopeObject; + } + + static void visitChildren(JSCell*, SlotVisitor&); + bool isDynamicScope(bool& requiresDynamicChecks) const; + static JSObject* toThisObject(JSCell*, ExecState*); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static void put(JSCell*, ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + static void putWithAttributes(JSObject*, ExecState*, const Identifier&, JSValue, unsigned attributes); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(StaticScopeObjectType, StructureFlags), &s_info); } + + static const ClassInfo s_info; + + protected: + void finishCreation(ExecState* exec, const Identifier& identifier, JSValue value, unsigned attributes) + { + Base::finishCreation(exec->globalData()); + m_registerStore.set(exec->globalData(), this, value); + symbolTable().add(identifier.impl(), SymbolTableEntry(-1, attributes)); + } + static const unsigned StructureFlags = IsEnvironmentRecord | OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; + + private: + JSStaticScopeObject(ExecState* exec) + : JSVariableObject(exec->globalData(), exec->globalData().staticScopeStructure.get(), &m_symbolTable, reinterpret_cast<Register*>(&m_registerStore + 1)) + { + } + + static void destroy(JSCell*); + + SymbolTable m_symbolTable; + WriteBarrier<Unknown> m_registerStore; + }; + + inline bool JSStaticScopeObject::isDynamicScope(bool&) const + { + return false; + } + +} + +#endif // JSStaticScopeObject_h diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp new file mode 100644 index 000000000..93193b5e5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSString.cpp @@ -0,0 +1,303 @@ +/* + * 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; + +const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) }; + +void JSString::RopeBuilder::expand() +{ + ASSERT(m_index == JSString::s_maxInternalRopeLength); + JSString* jsString = m_jsString; + m_jsString = jsStringBuilder(&m_globalData); + m_index = 0; + append(jsString); +} + +void JSString::destroy(JSCell* cell) +{ + JSString* thisObject = jsCast<JSString*>(cell); + thisObject->JSString::~JSString(); +} + +void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSString* thisObject = jsCast<JSString*>(cell); + Base::visitChildren(thisObject, visitor); + for (size_t i = 0; i < s_maxInternalRopeLength && thisObject->m_fibers[i]; ++i) + visitor.append(&thisObject->m_fibers[i]); +} + +void JSString::resolveRope(ExecState* exec) const +{ + ASSERT(isRope()); + + if (is8Bit()) { + LChar* buffer; + if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) + m_value = newImpl.release(); + else { + outOfMemory(exec); + return; + } + + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { + if (m_fibers[i]->isRope()) + return resolveRopeSlowCase8(buffer); + } + + LChar* position = buffer; + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { + StringImpl* string = m_fibers[i]->m_value.impl(); + unsigned length = string->length(); + StringImpl::copyChars(position, string->characters8(), length); + position += length; + m_fibers[i].clear(); + } + ASSERT((buffer + m_length) == position); + ASSERT(!isRope()); + + return; + } + + UChar* buffer; + if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) + m_value = newImpl.release(); + else { + outOfMemory(exec); + return; + } + + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { + if (m_fibers[i]->isRope()) + return resolveRopeSlowCase(buffer); + } + + UChar* position = buffer; + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { + StringImpl* string = m_fibers[i]->m_value.impl(); + unsigned length = string->length(); + StringImpl::copyChars(position, string->characters(), length); + position += length; + m_fibers[i].clear(); + } + ASSERT((buffer + m_length) == position); + ASSERT(!isRope()); +} + +// Overview: These functions convert 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::resolveRopeSlowCase8(LChar* buffer) const +{ + LChar* position = buffer + m_length; // We will be working backwards over the rope. + Vector<JSString*, 32> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method. + + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) { + workQueue.append(m_fibers[i].get()); + // Clearing here works only because there are no GC points in this method. + m_fibers[i].clear(); + } + + while (!workQueue.isEmpty()) { + JSString* currentFiber = workQueue.last(); + workQueue.removeLast(); + + if (currentFiber->isRope()) { + for (size_t i = 0; i < s_maxInternalRopeLength && currentFiber->m_fibers[i]; ++i) + workQueue.append(currentFiber->m_fibers[i].get()); + continue; + } + + StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl()); + unsigned length = string->length(); + position -= length; + StringImpl::copyChars(position, string->characters8(), length); + } + + ASSERT(buffer == position); + ASSERT(!isRope()); +} + +void JSString::resolveRopeSlowCase(UChar* buffer) const +{ + UChar* position = buffer + m_length; // We will be working backwards over the rope. + Vector<JSString*, 32> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK. + + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) + workQueue.append(m_fibers[i].get()); + + while (!workQueue.isEmpty()) { + JSString* currentFiber = workQueue.last(); + workQueue.removeLast(); + + if (currentFiber->isRope()) { + for (size_t i = 0; i < s_maxInternalRopeLength && currentFiber->m_fibers[i]; ++i) + workQueue.append(currentFiber->m_fibers[i].get()); + continue; + } + + StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl()); + unsigned length = string->length(); + position -= length; + StringImpl::copyChars(position, string->characters(), length); + } + + ASSERT(buffer == position); + ASSERT(!isRope()); +} + +void JSString::outOfMemory(ExecState* exec) const +{ + for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) + m_fibers[i].clear(); + ASSERT(!isRope()); + ASSERT(m_value == UString()); + if (exec) + throwOutOfMemoryError(exec); +} + +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) const +{ + 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, JSGlobalObject* globalObject, JSString* string) +{ + StringObject* object = new (NotNull, allocateCell<StringObject>(*exec->heap())) StringObject(exec->globalData(), globalObject->stringObjectStructure()); + object->finishCreation(exec->globalData(), string); + return object; +} + +JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const +{ + return StringObject::create(exec, globalObject, const_cast<JSString*>(this)); +} + +JSObject* JSString::toThisObject(JSCell* cell, ExecState* exec) +{ + return StringObject::create(exec, exec->lexicalGlobalObject(), jsCast<JSString*>(cell)); +} + +bool JSString::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSString* thisObject = jsCast<JSString*>(cell); + // The semantics here are really getPropertySlot, not getOwnPropertySlot. + // This function should only be called by JSValue::get. + if (thisObject->getStringPropertySlot(exec, propertyName, slot)) + return true; + if (propertyName == exec->propertyNames().underscoreProto) { + slot.setValue(exec->lexicalGlobalObject()->stringPrototype()); + return true; + } + slot.setBase(thisObject); + JSObject* object; + for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) { + object = asObject(prototype); + if (object->methodTable()->getOwnPropertySlot(object, 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::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSString* thisObject = jsCast<JSString*>(cell); + // The semantics here are really getPropertySlot, not getOwnPropertySlot. + // This function should only be called by JSValue::get. + if (thisObject->getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSString::getOwnPropertySlot(thisObject, 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 000000000..6700e2e41 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -0,0 +1,478 @@ +/* + * 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 "PropertyDescriptor.h" +#include "PropertySlot.h" +#include "Structure.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&); + + JSString* jsStringBuilder(JSGlobalData*); + + class JS_EXPORTCLASS JSString : public JSCell { + public: + friend class JIT; + friend class JSGlobalData; + friend class SpecializedThunkJIT; + friend struct ThunkHelpers; + friend JSString* jsStringBuilder(JSGlobalData*); + + typedef JSCell Base; + + static void destroy(JSCell*); + + class RopeBuilder { + public: + RopeBuilder(JSGlobalData& globalData) + : m_globalData(globalData) + , m_jsString(jsStringBuilder(&globalData)) + , m_index(0) + { + } + + void append(JSString* jsString) + { + if (m_index == JSString::s_maxInternalRopeLength) + expand(); + m_jsString->m_fibers[m_index++].set(m_globalData, m_jsString, jsString); + m_jsString->m_length += jsString->m_length; + m_jsString->m_is8Bit = m_jsString->m_is8Bit && jsString->m_is8Bit; + } + + JSString* release() + { + JSString* tmp = m_jsString; + m_jsString = 0; + return tmp; + } + + unsigned length() { return m_jsString->m_length; } + + private: + void expand(); + + JSGlobalData& m_globalData; + JSString* m_jsString; + size_t m_index; + }; + + private: + JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value) + : JSCell(globalData, globalData.stringStructure.get()) + , m_value(value) + { + } + + JSString(JSGlobalData& globalData) + : JSCell(globalData, globalData.stringStructure.get()) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + m_length = 0; + m_is8Bit = true; + } + + void finishCreation(JSGlobalData& globalData, size_t length) + { + ASSERT(!m_value.isNull()); + Base::finishCreation(globalData); + m_length = length; + m_is8Bit = m_value.impl()->is8Bit(); + } + + void finishCreation(JSGlobalData& globalData, size_t length, size_t cost) + { + ASSERT(!m_value.isNull()); + Base::finishCreation(globalData); + m_length = length; + m_is8Bit = m_value.impl()->is8Bit(); + Heap::heap(this)->reportExtraMemoryCost(cost); + } + + void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2) + { + Base::finishCreation(globalData); + m_length = s1->length() + s2->length(); + m_is8Bit = (s1->is8Bit() && s2->is8Bit()); + m_fibers[0].set(globalData, this, s1); + m_fibers[1].set(globalData, this, s2); + } + + void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3) + { + Base::finishCreation(globalData); + m_length = s1->length() + s2->length() + s3->length(); + m_is8Bit = (s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); + m_fibers[0].set(globalData, this, s1); + m_fibers[1].set(globalData, this, s2); + m_fibers[2].set(globalData, this, s3); + } + + static JSString* createNull(JSGlobalData& globalData) + { + JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData); + newString->finishCreation(globalData); + return newString; + } + + public: + static JSString* create(JSGlobalData& globalData, PassRefPtr<StringImpl> value) + { + ASSERT(value); + size_t length = value->length(); + size_t cost = value->cost(); + JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value); + newString->finishCreation(globalData, length, cost); + return newString; + } + static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2) + { + JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData); + newString->finishCreation(globalData, s1, s2); + return newString; + } + static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3) + { + JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData); + newString->finishCreation(globalData, s1, s2, s3); + return newString; + } + static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value) + { + ASSERT(value); + size_t length = value->length(); + JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value); + newString->finishCreation(globalData, length); + return newString; + } + + const UString& value(ExecState* exec) const + { + if (isRope()) + resolveRope(exec); + return m_value; + } + const UString& tryGetValue() const + { + if (isRope()) + resolveRope(0); + return m_value; + } + unsigned length() { return m_length; } + + JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + bool toBoolean(ExecState*) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + JSObject* toObject(ExecState*, JSGlobalObject*) const; + UString toString(ExecState*) const; + double toNumber(ExecState*) const; + + 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); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot), &s_info); + } + + static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); } + static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); } + + static const ClassInfo s_info; + + static void visitChildren(JSCell*, SlotVisitor&); + + private: + void resolveRope(ExecState*) const; + void resolveRopeSlowCase8(LChar*) const; + void resolveRopeSlowCase(UChar*) const; + void outOfMemory(ExecState*) const; + + static JSObject* toThisObject(JSCell*, ExecState*); + + // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + + static const unsigned s_maxInternalRopeLength = 3; + + // A string is represented either by a UString or a rope of fibers. + bool m_is8Bit : 1; + unsigned m_length; + mutable UString m_value; + mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers; + + bool isRope() const { return m_value.isNull(); } + bool is8Bit() const { return m_is8Bit; } + UString& string() { ASSERT(!isRope()); return m_value; } + + friend JSValue jsString(ExecState*, JSString*, JSString*); + friend JSValue jsString(ExecState*, Register*, unsigned count); + friend JSValue jsStringFromArguments(ExecState*, JSValue thisValue); + friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length); + }; + + JSString* asString(JSValue); + + 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 <= maxSingleCharacterString) + return globalData->smallStrings.singleCharacterString(globalData, c); + return JSString::create(*globalData, UString(&c, 1).impl()); + } + + inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) + { + JSGlobalData* globalData = &exec->globalData(); + ASSERT(offset < static_cast<unsigned>(s.length())); + UChar c = s[offset]; + if (c <= maxSingleCharacterString) + return globalData->smallStrings.singleCharacterString(globalData, c); + return JSString::create(*globalData, StringImpl::create(s.impl(), offset, 1)); + } + + inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) + { + ASSERT(s); + ASSERT(s[0]); + ASSERT(s[1]); + return JSString::create(*globalData, UString(s).impl()); + } + + inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s) + { + ASSERT(s.length() > 1); + return JSString::create(*globalData, s.impl()); + } + + 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[0]; + if (c <= maxSingleCharacterString) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return JSString::create(*globalData, s.impl()); + } + + 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); + return jsSubstring(globalData, s->value(exec), offset, length); + } + + inline JSString* jsSubstring8(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[offset]; + if (c <= maxSingleCharacterString) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return JSString::createHasOtherOwner(*globalData, StringImpl::create8(s.impl(), 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[offset]; + if (c <= maxSingleCharacterString) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return JSString::createHasOtherOwner(*globalData, StringImpl::create(s.impl(), offset, length)); + } + + 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[0]; + if (c <= maxSingleCharacterString) + return globalData->smallStrings.singleCharacterString(globalData, c); + } + return JSString::createHasOtherOwner(*globalData, s.impl()); + } + + inline JSString* jsStringBuilder(JSGlobalData* globalData) + { + return JSString::createNull(*globalData); + } + + 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* jsSubstring8(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->globalData(), s, offset, length); } + 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(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; } + + inline bool JSCell::toBoolean(ExecState* exec) const + { + if (isString()) + return static_cast<const JSString*>(this)->toBoolean(exec); + return !structure()->typeInfo().masqueradesAsUndefined(); + } + + // --- JSValue inlines ---------------------------- + + inline bool JSValue::toBoolean(ExecState* exec) const + { + if (isInt32()) + return asInt32(); + 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. + } + + 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); + } + +} // namespace JSC + +#endif // JSString_h diff --git a/Source/JavaScriptCore/runtime/JSStringBuilder.h b/Source/JavaScriptCore/runtime/JSStringBuilder.h new file mode 100644 index 000000000..b7e7e781e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringBuilder.h @@ -0,0 +1,194 @@ +/* + * 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) + , m_is8Bit(true) + { + } + + void append(const UChar u) + { + if (m_is8Bit) { + if (u < 0xff) { + LChar c = u; + m_okay &= buffer8.tryAppend(&c, 1); + return; + } + upConvert(); + } + m_okay &= buffer16.tryAppend(&u, 1); + } + + void append(const char* str) + { + append(str, strlen(str)); + } + + void append(const char* str, size_t len) + { + if (m_is8Bit) { + m_okay &= buffer8.tryAppend(reinterpret_cast<const LChar*>(str), len); + return; + } + m_okay &= buffer8.tryReserveCapacity(buffer16.size() + len); + for (size_t i = 0; i < len; i++) { + UChar u = static_cast<unsigned char>(str[i]); + m_okay &= buffer16.tryAppend(&u, 1); + } + } + + void append(const LChar* str, size_t len) + { + if (m_is8Bit) { + m_okay &= buffer8.tryAppend(str, len); + return; + } + m_okay &= buffer8.tryReserveCapacity(buffer16.size() + len); + for (size_t i = 0; i < len; i++) { + UChar u = str[i]; + m_okay &= buffer16.tryAppend(&u, 1); + } + } + + void append(const UChar* str, size_t len) + { + if (m_is8Bit) + upConvert(); // FIXME: We could check character by character its size. + m_okay &= buffer16.tryAppend(str, len); + } + + void append(const UString& str) + { + unsigned length = str.length(); + + if (!length) + return; + + if (m_is8Bit) { + if (str.is8Bit()) { + m_okay &= buffer8.tryAppend(str.characters8(), length); + return; + } + upConvert(); + } + m_okay &= buffer16.tryAppend(str.characters(), length); + } + + void upConvert() + { + ASSERT(m_is8Bit); + size_t len = buffer8.size(); + + for (size_t i = 0; i < len; i++) + buffer16.append(buffer8[i]); + + buffer8.clear(); + m_is8Bit = false; + } + + JSValue build(ExecState* exec) + { + if (!m_okay) + return throwOutOfMemoryError(exec); + if (m_is8Bit) { + buffer8.shrinkToFit(); + if (!buffer8.data()) + return throwOutOfMemoryError(exec); + return jsString(exec, UString::adopt(buffer8)); + } + buffer16.shrinkToFit(); + if (!buffer16.data()) + return throwOutOfMemoryError(exec); + return jsString(exec, UString::adopt(buffer16)); + } + +protected: + Vector<LChar, 64> buffer8; + Vector<UChar, 64> buffer16; + bool m_okay; + bool m_is8Bit; +}; + +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 000000000..84a27a7ea --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSType.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 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 { + +enum JSType { + UnspecifiedType, + UndefinedType, + BooleanType, + NumberType, + NullType, + StringType, + LeafType, + + // The CompoundType value must come before any JSType that may have children. + CompoundType, + GetterSetterType, + APIValueWrapperType, + + EvalExecutableType, + ProgramExecutableType, + FunctionExecutableType, + + // The ObjectType value must come before any JSType that is a subclass of JSObject. + ObjectType, + FinalObjectType, + JSFunctionType, + NumberObjectType, + ErrorInstanceType, + GlobalThisType, + + // VariableObjectType must come before all of the types of its subclasses and only its subclasses. + VariableObjectType, + GlobalObjectType, + ActivationObjectType, + StaticScopeObjectType, +}; + +} // namespace JSC + +#endif diff --git a/Source/JavaScriptCore/runtime/JSTypeInfo.h b/Source/JavaScriptCore/runtime/JSTypeInfo.h new file mode 100644 index 000000000..3e23aa253 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypeInfo.h @@ -0,0 +1,100 @@ +// -*- 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 { + + static const unsigned MasqueradesAsUndefined = 1; // WebCore uses MasqueradesAsUndefined to make document.all undetectable. + static const unsigned ImplementsHasInstance = 1 << 1; + static const unsigned OverridesHasInstance = 1 << 2; + static const unsigned ImplementsDefaultHasInstance = 1 << 3; + static const unsigned IsEnvironmentRecord = 1 << 4; + static const unsigned OverridesGetOwnPropertySlot = 1 << 5; + static const unsigned OverridesVisitChildren = 1 << 6; + static const unsigned OverridesGetPropertyNames = 1 << 7; + static const unsigned ProhibitsPropertyCaching = 1 << 8; + + class TypeInfo { + public: + TypeInfo(JSType type, unsigned flags = 0) + : m_type(type) + , m_flags(flags & 0xff) + , m_flags2(flags >> 8) + { + ASSERT(flags <= 0x3ff); + ASSERT(type <= 0xff); + ASSERT(type >= CompoundType || !(flags & OverridesVisitChildren)); + // No object that doesn't ImplementsHasInstance should override it! + ASSERT((m_flags & (ImplementsHasInstance | OverridesHasInstance)) != OverridesHasInstance); + // ImplementsDefaultHasInstance means (ImplementsHasInstance & !OverridesHasInstance) + if ((m_flags & (ImplementsHasInstance | OverridesHasInstance)) == ImplementsHasInstance) + m_flags |= ImplementsDefaultHasInstance; + } + + JSType type() const { return static_cast<JSType>(m_type); } + bool isObject() const { return type() >= ObjectType; } + bool isFinalObject() const { return type() == FinalObjectType; } + bool isNumberObject() const { return type() == NumberObjectType; } + + bool masqueradesAsUndefined() const { return isSetOnFlags1(MasqueradesAsUndefined); } + bool implementsHasInstance() const { return isSetOnFlags1(ImplementsHasInstance); } + bool isEnvironmentRecord() const { return isSetOnFlags1(IsEnvironmentRecord); } + bool overridesHasInstance() const { return isSetOnFlags1(OverridesHasInstance); } + bool implementsDefaultHasInstance() const { return isSetOnFlags1(ImplementsDefaultHasInstance); } + bool overridesGetOwnPropertySlot() const { return isSetOnFlags1(OverridesGetOwnPropertySlot); } + bool overridesVisitChildren() const { return isSetOnFlags1(OverridesVisitChildren); } + bool overridesGetPropertyNames() const { return isSetOnFlags1(OverridesGetPropertyNames); } + bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); } + + static ptrdiff_t flagsOffset() + { + return OBJECT_OFFSETOF(TypeInfo, m_flags); + } + + static ptrdiff_t typeOffset() + { + return OBJECT_OFFSETOF(TypeInfo, m_type); + } + + private: + bool isSetOnFlags1(unsigned flag) const { ASSERT(flag <= (1 << 7)); return m_flags & flag; } + bool isSetOnFlags2(unsigned flag) const { ASSERT(flag >= (1 << 8)); return m_flags2 & (flag >> 8); } + + unsigned char m_type; + unsigned char m_flags; + unsigned char m_flags2; + }; + +} + +#endif // JSTypeInfo_h diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp new file mode 100644 index 000000000..a5d3d936a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -0,0 +1,231 @@ +/* + * 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)); +} + +double JSValue::toNumberSlowCase(ExecState* exec) const +{ + ASSERT(!isInt32() && !isDouble()); + if (isCell()) + return asCell()->toNumber(exec); + if (isTrue()) + return 1.0; + return isUndefined() ? std::numeric_limits<double>::quiet_NaN() : 0; // null and false both convert to 0. +} + +JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, globalObject, asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, globalObject, asValue()); + + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return JSNotAnObject::create(exec); +} + +JSObject* JSValue::toThisObjectSlowCase(ExecState* exec) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, exec->lexicalGlobalObject(), asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, exec->lexicalGlobalObject(), asValue()); + ASSERT(isUndefinedOrNull()); + return exec->globalThisValue(); +} + +JSObject* JSValue::synthesizeObject(ExecState* exec) const +{ + ASSERT(!isCell()); + if (isNumber()) + return constructNumber(exec, exec->lexicalGlobalObject(), asValue()); + if (isBoolean()) + return constructBooleanFromImmediateBoolean(exec, exec->lexicalGlobalObject(), asValue()); + + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return JSNotAnObject::create(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 JSNotAnObject::create(exec); +} + +#ifndef NDEBUG +char* JSValue::description() +{ + static const size_t size = 64; + static char description[size]; + + if (!*this) + snprintf(description, size, "<JSValue()>"); + else if (isInt32()) + snprintf(description, size, "Int32: %d", asInt32()); + else if (isDouble()) { +#if USE(JSVALUE64) + snprintf(description, size, "Double: %lf, %lx", asDouble(), reinterpretDoubleToIntptr(asDouble())); +#else + union { + double asDouble; + uint32_t asTwoInt32s[2]; + } u; + u.asDouble = asDouble(); + snprintf(description, size, "Double: %lf, %08x:%08x", asDouble(), u.asTwoInt32s[1], u.asTwoInt32s[0]); +#endif + } 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; +} + +bool JSValue::isValidCallee() +{ + return asObject(asCell())->globalObject(); +} + +JSString* JSValue::toPrimitiveString(ExecState* exec) const +{ + if (isString()) + return static_cast<JSString*>(asCell()); + if (isInt32()) + return jsString(&exec->globalData(), exec->globalData().numericStrings.add(asInt32())); + if (isDouble()) + return jsString(&exec->globalData(), exec->globalData().numericStrings.add(asDouble())); + if (isTrue()) + return jsNontrivialString(exec, exec->propertyNames().trueKeyword.ustring()); + if (isFalse()) + return jsNontrivialString(exec, exec->propertyNames().falseKeyword.ustring()); + if (isNull()) + return jsNontrivialString(exec, exec->propertyNames().nullKeyword.ustring()); + if (isUndefined()) + return jsNontrivialString(exec, exec->propertyNames().undefined.ustring()); + + ASSERT(isCell()); + JSValue v = asCell()->toPrimitive(exec, NoPreference); + if (v.isString()) + return static_cast<JSString*>(v.asCell()); + return jsString(&exec->globalData(), v.toString(exec)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSValue.h b/Source/JavaScriptCore/runtime/JSValue.h new file mode 100644 index 000000000..a00106274 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSValue.h @@ -0,0 +1,478 @@ +/* + * 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/HashMap.h> +#include <wtf/HashTraits.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + + class ExecState; + class Identifier; + class JSCell; + class JSGlobalData; + class JSGlobalObject; + class JSObject; + class JSString; + class PropertySlot; + class PutPropertySlot; + class UString; +#if ENABLE(DFG_JIT) + namespace DFG { + class AssemblyHelpers; + class JITCompiler; + class JITCodeGenerator; + class JSValueSource; + class OSRExitCompiler; + class SpeculativeJIT; + } +#endif + + struct ClassInfo; + struct Instruction; + struct MethodTable; + + template <class T> class WriteBarrierBase; + + enum PreferredPrimitiveType { NoPreference, PreferNumber, PreferString }; + + +#if USE(JSVALUE32_64) + typedef int64_t EncodedJSValue; +#else + typedef void* EncodedJSValue; +#endif + + union EncodedValueDescriptor { + int64_t asInt64; +#if USE(JSVALUE32_64) + double asDouble; +#elif USE(JSVALUE64) + JSCell* ptr; +#endif + +#if CPU(BIG_ENDIAN) + struct { + int32_t tag; + int32_t payload; + } asBits; +#else + struct { + int32_t payload; + int32_t tag; + } asBits; +#endif + }; + + // 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 struct EncodedJSValueHashTraits; + friend class JIT; + friend class JITStubs; + friend class JITStubCall; + friend class JSInterfaceJIT; + friend class SpecializedThunkJIT; +#if ENABLE(DFG_JIT) + friend class DFG::AssemblyHelpers; + friend class DFG::JITCompiler; + friend class DFG::JITCodeGenerator; + friend class DFG::JSValueSource; + friend class DFG::OSRExitCompiler; + friend class DFG::SpeculativeJIT; +#endif + + public: + static EncodedJSValue encode(JSValue); + static JSValue decode(EncodedJSValue); + + 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; + bool asBoolean() const; + double asNumber() const; + + // Querying the type. + bool isEmpty() const; + bool isUndefined() const; + bool isNull() const; + bool isUndefinedOrNull() const; + bool isBoolean() const; + bool isNumber() const; + bool isString() const; + bool isPrimitive() const; + bool isGetterSetter() const; + bool isObject() const; + bool inherits(const ClassInfo*) const; + + // Extracting the value. + 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; + UString toString(ExecState*) const; + JSString* toPrimitiveString(ExecState*) const; + JSObject* toObject(ExecState*) const; + JSObject* toObject(ExecState*, JSGlobalObject*) const; + + // Integer conversions. + double toInteger(ExecState*) const; + double toIntegerPreserveNaN(ExecState*) const; + int32_t toInt32(ExecState*) const; + uint32_t toUInt32(ExecState*) const; + + // 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); + + JSObject* toThisObject(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); + + bool isCell() const; + JSCell* asCell() const; + bool isValidCallee(); + +#ifndef NDEBUG + char* description(); +#endif + + private: + template <class T> JSValue(WriteBarrierBase<T>); + + enum HashTableDeletedValueTag { HashTableDeletedValue }; + JSValue(HashTableDeletedValueTag); + + inline const JSValue asValue() const { return *this; } + double toNumberSlowCase(ExecState*) const; + JSObject* toObjectSlowCase(ExecState*, JSGlobalObject*) const; + JSObject* toThisObjectSlowCase(ExecState*) const; + + JSObject* synthesizePrototype(ExecState*) const; + JSObject* synthesizeObject(ExecState*) const; + +#if USE(JSVALUE32_64) + /* + * On 32-bit platforms USE(JSVALUE32_64) should be defined, and we use a NaN-encoded + * form for immediates. + * + * 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). + * + * For JSValues that do not contain a double value, the high 32 bits contain the tag + * values listed in the enums below, which all correspond to NaN-space. In the case of + * cell, integer and bool values the lower 32 bits (the 'payload') contain the pointer + * integer or boolean value; in the case of all other tags the payload is 0. + */ + enum { Int32Tag = 0xffffffff }; + enum { BooleanTag = 0xfffffffe }; + enum { NullTag = 0xfffffffd }; + enum { UndefinedTag = 0xfffffffc }; + enum { CellTag = 0xfffffffb }; + enum { EmptyValueTag = 0xfffffffa }; + enum { DeletedValueTag = 0xfffffff9 }; + + enum { LowestTag = DeletedValueTag }; + + uint32_t tag() const; + int32_t payload() const; +#elif USE(JSVALUE64) + /* + * On 64-bit platforms USE(JSVALUE64) should be defined, and we use a NaN-encoded + * form for immediates. + * + * 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 top 16-bits denote the type of the encoded JSValue: + * + * Pointer { 0000:PPPP:PPPP:PPPP + * / 0001:****:****:**** + * Double { ... + * \ FFFE:****:****:**** + * Integer { FFFF:0000:IIII:IIII + * + * The scheme we have implemented encodes double precision values by performing a + * 64-bit integer addition of the value 2^48 to the number. After this manipulation + * no encoded double-precision value will begin with the pattern 0x0000 or 0xFFFF. + * Values must be decoded by reversing this operation before subsequent floating point + * operations my be peformed. + * + * 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 represented by specific, invalid pointer values: + * + * False: 0x06 + * True: 0x07 + * Undefined: 0x0a + * Null: 0x02 + * + * These values have the following properties: + * - Bit 1 (TagBitTypeOther) is set for all four values, allowing real pointers to be + * quickly distinguished from all immediate values, including these invalid pointers. + * - With bit 3 is masked out (TagBitUndefined) Undefined and Null share the + * same value, allowing null & undefined to be quickly detected. + * + * No valid JSValue will have the bit pattern 0x0, this is used to represent array + * holes, and as a C++ 'no value' result (e.g. JSValue() has an internal value of 0). + */ + + // These values are #defines since using static const integers here is a ~1% regression! + + // 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. + #define DoubleEncodeOffset 0x1000000000000ll + // 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. + #define TagTypeNumber 0xffff000000000000ll + + // All non-numeric (bool, null, undefined) immediates have bit 2 set. + #define TagBitTypeOther 0x2ll + #define TagBitBool 0x4ll + #define TagBitUndefined 0x8ll + // Combined integer value for non-numeric immediates. + #define ValueFalse (TagBitTypeOther | TagBitBool | false) + #define ValueTrue (TagBitTypeOther | TagBitBool | true) + #define ValueUndefined (TagBitTypeOther | TagBitUndefined) + #define ValueNull (TagBitTypeOther) + + // TagMask is used to check for all types of immediate values (either number or 'other'). + #define TagMask (TagTypeNumber | TagBitTypeOther) + + // These special values are never visible to JavaScript code; Empty is used to represent + // Array holes, and for uninitialized JSValues. Deleted is used in hash table code. + // These values would map to cell types in the JSValue encoding, but not valid GC cell + // pointer should have either of these values (Empty is null, deleted is at an invalid + // alignment for a GC cell, and in the zero page). + #define ValueEmpty 0x0ll + #define ValueDeleted 0x4ll +#endif + + EncodedValueDescriptor u; + }; + +#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 + + typedef HashMap<EncodedJSValue, unsigned, EncodedJSValueHash, EncodedJSValueHashTraits> JSValueMap; + + // 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) + { + ASSERT(JSValue(JSValue::EncodeAsDouble, d).isNumber()); + return JSValue(JSValue::EncodeAsDouble, d); + } + + ALWAYS_INLINE JSValue jsNumber(double d) + { + ASSERT(JSValue(d).isNumber()); + 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; } + +} // namespace JSC + +#endif // JSValue_h diff --git a/Source/JavaScriptCore/runtime/JSValueInlineMethods.h b/Source/JavaScriptCore/runtime/JSValueInlineMethods.h new file mode 100644 index 000000000..e13d34745 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSValueInlineMethods.h @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2011 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 JSValueInlineMethods_h +#define JSValueInlineMethods_h + +#include "JSValue.h" + +namespace JSC { + + 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); + } + + inline bool JSValue::isUInt32() const + { + return isInt32() && asInt32() >= 0; + } + + inline uint32_t JSValue::asUInt32() const + { + ASSERT(isUInt32()); + return asInt32(); + } + + inline double JSValue::asNumber() const + { + ASSERT(isNumber()); + return isInt32() ? asInt32() : asDouble(); + } + + inline JSValue jsNaN() + { + return JSValue(std::numeric_limits<double>::quiet_NaN()); + } + + 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(unsigned i) + { + if (static_cast<int32_t>(i) < 0) { + *this = JSValue(EncodeAsDouble, 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(EncodeAsDouble, 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(EncodeAsDouble, 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(EncodeAsDouble, 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(EncodeAsDouble, static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<uint32_t>(i)); + } + + inline JSValue::JSValue(double d) + { + const int32_t asInt32 = static_cast<int32_t>(d); + if (asInt32 != d || (!asInt32 && signbit(d))) { // true for -0.0 + *this = JSValue(EncodeAsDouble, d); + return; + } + *this = JSValue(static_cast<int32_t>(d)); + } + +#if USE(JSVALUE32_64) + inline EncodedJSValue JSValue::encode(JSValue value) + { + return value.u.asInt64; + } + + inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) + { + JSValue v; + v.u.asInt64 = encodedJSValue; + 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 = BooleanTag; + u.asBits.payload = 1; + } + + inline JSValue::JSValue(JSFalseTag) + { + u.asBits.tag = BooleanTag; + 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); + } + + 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)); + } + + inline JSValue::operator bool() const + { + ASSERT(tag() != DeletedValueTag); + return tag() != EmptyValueTag; + } + + inline bool JSValue::operator==(const JSValue& other) const + { + return u.asInt64 == other.u.asInt64; + } + + inline bool JSValue::operator!=(const JSValue& other) const + { + return u.asInt64 != other.u.asInt64; + } + + inline bool JSValue::isEmpty() const + { + return tag() == EmptyValueTag; + } + + 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::isDouble() const + { + return tag() < LowestTag; + } + + inline bool JSValue::isTrue() const + { + return tag() == BooleanTag && payload(); + } + + inline bool JSValue::isFalse() const + { + return tag() == BooleanTag && !payload(); + } + + 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 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(int i) + { + u.asBits.tag = Int32Tag; + u.asBits.payload = i; + } + + inline bool JSValue::isNumber() const + { + return isInt32() || isDouble(); + } + + inline bool JSValue::isBoolean() const + { + return isTrue() || isFalse(); + } + + inline bool JSValue::asBoolean() const + { + ASSERT(isBoolean()); + return payload(); + } + +#else // USE(JSVALUE32_64) + + // JSValue member functions. + inline EncodedJSValue JSValue::encode(JSValue value) + { + return value.u.ptr; + } + + inline JSValue JSValue::decode(EncodedJSValue ptr) + { + return JSValue(reinterpret_cast<JSCell*>(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() + { + u.asInt64 = ValueEmpty; + } + + // 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) + { + u.asInt64 = ValueDeleted; + } + + inline JSValue::JSValue(JSCell* ptr) + { + u.ptr = ptr; + } + + inline JSValue::JSValue(const JSCell* ptr) + { + u.ptr = const_cast<JSCell*>(ptr); + } + + inline JSValue::operator bool() const + { + return u.ptr; + } + + inline bool JSValue::operator==(const JSValue& other) const + { + return u.ptr == other.u.ptr; + } + + inline bool JSValue::operator!=(const JSValue& other) const + { + return u.ptr != other.u.ptr; + } + + inline bool JSValue::isEmpty() const + { + return u.asInt64 == ValueEmpty; + } + + inline bool JSValue::isUndefined() const + { + return asValue() == JSValue(JSUndefined); + } + + inline bool JSValue::isNull() const + { + return asValue() == JSValue(JSNull); + } + + inline bool JSValue::isTrue() const + { + return asValue() == JSValue(JSTrue); + } + + inline bool JSValue::isFalse() const + { + return asValue() == JSValue(JSFalse); + } + + inline bool JSValue::asBoolean() const + { + ASSERT(isBoolean()); + return asValue() == JSValue(JSTrue); + } + + inline int32_t JSValue::asInt32() const + { + ASSERT(isInt32()); + return static_cast<int32_t>(u.asInt64); + } + + inline bool JSValue::isDouble() const + { + return isNumber() && !isInt32(); + } + + inline JSValue::JSValue(JSNullTag) + { + u.asInt64 = ValueNull; + } + + inline JSValue::JSValue(JSUndefinedTag) + { + u.asInt64 = ValueUndefined; + } + + inline JSValue::JSValue(JSTrueTag) + { + u.asInt64 = ValueTrue; + } + + inline JSValue::JSValue(JSFalseTag) + { + u.asInt64 = ValueFalse; + } + + inline bool JSValue::isUndefinedOrNull() const + { + // Undefined and null share the same value, bar the 'undefined' bit in the extended tag. + return (u.asInt64 & ~TagBitUndefined) == ValueNull; + } + + inline bool JSValue::isBoolean() const + { + return (u.asInt64 & ~1) == ValueFalse; + } + + inline bool JSValue::isCell() const + { + return !(u.asInt64 & TagMask); + } + + inline bool JSValue::isInt32() const + { + return (u.asInt64 & TagTypeNumber) == TagTypeNumber; + } + + inline intptr_t reinterpretDoubleToIntptr(double value) + { + return bitwise_cast<intptr_t>(value); + } + inline double reinterpretIntptrToDouble(intptr_t value) + { + return bitwise_cast<double>(value); + } + + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) + { + u.asInt64 = reinterpretDoubleToIntptr(d) + DoubleEncodeOffset; + } + + inline JSValue::JSValue(int i) + { + u.asInt64 = TagTypeNumber | static_cast<uint32_t>(i); + } + + inline double JSValue::asDouble() const + { + return reinterpretIntptrToDouble(u.asInt64 - DoubleEncodeOffset); + } + + inline bool JSValue::isNumber() const + { + return u.asInt64 & TagTypeNumber; + } + + ALWAYS_INLINE JSCell* JSValue::asCell() const + { + ASSERT(isCell()); + return u.ptr; + } + +#endif // USE(JSVALUE64) + +} // namespace JSC + +#endif // JSValueInlineMethods_h diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.cpp b/Source/JavaScriptCore/runtime/JSVariableObject.cpp new file mode 100644 index 000000000..706e3debb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSVariableObject.cpp @@ -0,0 +1,98 @@ +/* + * 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 "JSActivation.h" +#include "JSGlobalObject.h" +#include "JSStaticScopeObject.h" +#include "PropertyNameArray.h" +#include "PropertyDescriptor.h" + +namespace JSC { + +void JSVariableObject::destroy(JSCell* cell) +{ + jsCast<JSVariableObject*>(cell)->JSVariableObject::~JSVariableObject(); +} + +bool JSVariableObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + JSVariableObject* thisObject = jsCast<JSVariableObject*>(cell); + if (thisObject->symbolTable().contains(propertyName.impl())) + return false; + + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +void JSVariableObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSVariableObject* thisObject = jsCast<JSVariableObject*>(object); + SymbolTable::const_iterator end = thisObject->symbolTable().end(); + for (SymbolTable::const_iterator it = thisObject->symbolTable().begin(); it != end; ++it) { + if (!(it->second.getAttributes() & DontEnum) || (mode == IncludeDontEnumProperties)) + propertyNames.add(Identifier(exec, it->first.get())); + } + + JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); +} + +bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + descriptor.setDescriptor(registerAt(entry.getIndex()).get(), entry.getAttributes() | DontDelete); + return true; + } + return false; +} + +void JSVariableObject::putWithAttributes(JSObject*, ExecState*, const Identifier&, JSValue, unsigned) +{ + ASSERT_NOT_REACHED(); +} + +bool JSVariableObject::isDynamicScope(bool& requiresDynamicChecks) const +{ + switch (structure()->typeInfo().type()) { + case GlobalObjectType: + return static_cast<const JSGlobalObject*>(this)->isDynamicScope(requiresDynamicChecks); + case ActivationObjectType: + return static_cast<const JSActivation*>(this)->isDynamicScope(requiresDynamicChecks); + case StaticScopeObjectType: + return static_cast<const JSStaticScopeObject*>(this)->isDynamicScope(requiresDynamicChecks); + default: + ASSERT_NOT_REACHED(); + break; + } + + return false; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.h b/Source/JavaScriptCore/runtime/JSVariableObject.h new file mode 100644 index 000000000..78e624e02 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSVariableObject.h @@ -0,0 +1,173 @@ +/* + * 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 JSNonFinalObject { + friend class JIT; + + public: + typedef JSNonFinalObject Base; + + SymbolTable& symbolTable() const { return *m_symbolTable; } + + static void destroy(JSCell*); + + static NO_RETURN_DUE_TO_ASSERT void putWithAttributes(JSObject*, ExecState*, const Identifier&, JSValue, unsigned attributes); + + static bool deleteProperty(JSCell*, ExecState*, const Identifier&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + bool isDynamicScope(bool& requiresDynamicChecks) const; + + WriteBarrier<Unknown>& registerAt(int index) const { return m_registers[index]; } + + WriteBarrier<Unknown>* const * addressOfRegisters() const { return &m_registers; } + static size_t offsetOfRegisters() { return OBJECT_OFFSETOF(JSVariableObject, m_registers); } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(VariableObjectType, StructureFlags), &s_info); + } + + protected: + static const unsigned StructureFlags = OverridesGetPropertyNames | JSNonFinalObject::StructureFlags; + + JSVariableObject(JSGlobalData& globalData, Structure* structure, SymbolTable* symbolTable, Register* registers) + : JSNonFinalObject(globalData, structure) + , m_symbolTable(symbolTable) + , m_registers(reinterpret_cast<WriteBarrier<Unknown>*>(registers)) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + ASSERT(m_symbolTable); + COMPILE_ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(Register), Register_should_be_same_size_as_WriteBarrier); + } + + PassOwnArrayPtr<WriteBarrier<Unknown> > copyRegisterArray(JSGlobalData&, WriteBarrier<Unknown>* src, size_t count, size_t callframeStarts); + void setRegisters(WriteBarrier<Unknown>* registers, PassOwnArrayPtr<WriteBarrier<Unknown> > registerArray); + + bool symbolTableGet(const Identifier&, PropertySlot&); + bool symbolTableGet(const Identifier&, PropertyDescriptor&); + bool symbolTableGet(const Identifier&, PropertySlot&, bool& slotIsWriteable); + bool symbolTablePut(ExecState*, const Identifier&, JSValue, bool shouldThrow); + bool symbolTablePutWithAttributes(JSGlobalData&, const Identifier&, JSValue, unsigned attributes); + + SymbolTable* m_symbolTable; // Maps name -> offset from "r" in register file. + WriteBarrier<Unknown>* m_registers; // "r" in the register file. + OwnArrayPtr<WriteBarrier<Unknown> > m_registerArray; // Independent copy of registers, used when a variable object copies its registers out of the register file. + }; + + inline bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) + { + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + slot.setValue(registerAt(entry.getIndex()).get()); + 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.setValue(registerAt(entry.getIndex()).get()); + slotIsWriteable = !entry.isReadOnly(); + return true; + } + return false; + } + + inline bool JSVariableObject::symbolTablePut(ExecState* exec, const Identifier& propertyName, JSValue value, bool shouldThrow) + { + JSGlobalData& globalData = exec->globalData(); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (entry.isNull()) + return false; + if (entry.isReadOnly()) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return true; + } + registerAt(entry.getIndex()).set(globalData, this, value); + return true; + } + + inline bool JSVariableObject::symbolTablePutWithAttributes(JSGlobalData& globalData, 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()).set(globalData, this, value); + return true; + } + + inline PassOwnArrayPtr<WriteBarrier<Unknown> > JSVariableObject::copyRegisterArray(JSGlobalData& globalData, WriteBarrier<Unknown>* src, size_t count, size_t callframeStarts) + { + OwnArrayPtr<WriteBarrier<Unknown> > registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[count]); + for (size_t i = 0; i < callframeStarts; i++) + registerArray[i].set(globalData, this, src[i].get()); + for (size_t i = callframeStarts + RegisterFile::CallFrameHeaderSize; i < count; i++) + registerArray[i].set(globalData, this, src[i].get()); + + return registerArray.release(); + } + + inline void JSVariableObject::setRegisters(WriteBarrier<Unknown>* registers, PassOwnArrayPtr<WriteBarrier<Unknown> > registerArray) + { + ASSERT(registerArray != m_registerArray); + m_registerArray = registerArray; + m_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 000000000..f8f5727d8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp @@ -0,0 +1,41 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSWrapperObject); + +void JSWrapperObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSWrapperObject* thisObject = jsCast<JSWrapperObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + JSObject::visitChildren(thisObject, visitor); + if (thisObject->m_internalValue) + visitor.append(&thisObject->m_internalValue); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.h b/Source/JavaScriptCore/runtime/JSWrapperObject.h new file mode 100644 index 000000000..65b4bdb7f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.h @@ -0,0 +1,72 @@ +/* + * 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 JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + JSValue internalValue() const; + void setInternalValue(JSGlobalData&, JSValue); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + explicit JSWrapperObject(JSGlobalData&, Structure*); + static const unsigned StructureFlags = OverridesVisitChildren | JSNonFinalObject::StructureFlags; + + static void visitChildren(JSCell*, SlotVisitor&); + + private: + WriteBarrier<Unknown> m_internalValue; + }; + + inline JSWrapperObject::JSWrapperObject(JSGlobalData& globalData, Structure* structure) + : JSNonFinalObject(globalData, structure) + { + } + + inline JSValue JSWrapperObject::internalValue() const + { + return m_internalValue.get(); + } + + inline void JSWrapperObject::setInternalValue(JSGlobalData& globalData, JSValue value) + { + ASSERT(value); + ASSERT(!value.isObject()); + m_internalValue.set(globalData, this, value); + } + +} // namespace JSC + +#endif // JSWrapperObject_h diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp new file mode 100644 index 000000000..b22b81503 --- /dev/null +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -0,0 +1,829 @@ +/* + * 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 "StrongInlines.h" +#include "UStringBuilder.h" +#include <wtf/ASCIICType.h> +#include <wtf/dtoa.h> + +namespace JSC { + +template <typename CharType> +static inline bool isJSONWhiteSpace(const CharType& 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; +} + +template <typename CharType> +bool LiteralParser<CharType>::tryJSONPParse(Vector<JSONPData>& results, bool needsFullSourceInfo) +{ + if (m_lexer.next() != TokIdentifier) + return false; + do { + Vector<JSONPPathEntry> path; + // Unguarded next to start off the lexer + Identifier name = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + JSONPPathEntry entry; + if (name == m_exec->globalData().propertyNames->varKeyword) { + if (m_lexer.next() != TokIdentifier) + return false; + entry.m_type = JSONPPathEntryTypeDeclare; + entry.m_pathEntryName = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + path.append(entry); + } else { + entry.m_type = JSONPPathEntryTypeDot; + entry.m_pathEntryName = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + path.append(entry); + } + if (m_exec->globalData().keywords->isKeyword(entry.m_pathEntryName)) + return false; + TokenType tokenType = m_lexer.next(); + while (tokenType != TokAssign) { + switch (tokenType) { + case TokLBracket: { + entry.m_type = JSONPPathEntryTypeLookup; + if (m_lexer.next() != TokNumber) + return false; + double doubleIndex = m_lexer.currentToken().numberToken; + int index = (int)doubleIndex; + if (index != doubleIndex || index < 0) + return false; + entry.m_pathIndex = index; + if (m_lexer.next() != TokRBracket) + return false; + break; + } + case TokDot: { + entry.m_type = JSONPPathEntryTypeDot; + if (m_lexer.next() != TokIdentifier) + return false; + entry.m_pathEntryName = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + break; + } + case TokLParen: { + if (path.last().m_type != JSONPPathEntryTypeDot || needsFullSourceInfo) + return false; + path.last().m_type = JSONPPathEntryTypeCall; + entry = path.last(); + goto startJSON; + } + default: + return false; + } + path.append(entry); + tokenType = m_lexer.next(); + } + startJSON: + m_lexer.next(); + results.append(JSONPData()); + results.last().m_value.set(m_exec->globalData(), parse(StartParseExpression)); + if (!results.last().m_value) + return false; + results.last().m_path.swap(path); + if (entry.m_type == JSONPPathEntryTypeCall) { + if (m_lexer.currentToken().type != TokRParen) + return false; + m_lexer.next(); + } + if (m_lexer.currentToken().type != TokSemi) + break; + m_lexer.next(); + } while (m_lexer.currentToken().type == TokIdentifier); + return m_lexer.currentToken().type == TokEnd; +} + +template <typename CharType> +ALWAYS_INLINE const Identifier LiteralParser<CharType>::makeIdentifier(const LChar* characters, size_t length) +{ + if (!length) + return m_exec->globalData().propertyNames->emptyIdentifier; + if (characters[0] >= MaximumCachableCharacter) + return Identifier(&m_exec->globalData(), characters, length); + + if (length == 1) { + if (!m_shortIdentifiers[characters[0]].isNull()) + return m_shortIdentifiers[characters[0]]; + m_shortIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); + return m_shortIdentifiers[characters[0]]; + } + if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length)) + return m_recentIdentifiers[characters[0]]; + m_recentIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); + return m_recentIdentifiers[characters[0]]; +} + +template <typename CharType> +ALWAYS_INLINE const Identifier LiteralParser<CharType>::makeIdentifier(const UChar* characters, size_t length) +{ + if (!length) + return m_exec->globalData().propertyNames->emptyIdentifier; + if (characters[0] >= MaximumCachableCharacter) + return Identifier(&m_exec->globalData(), characters, length); + + if (length == 1) { + if (!m_shortIdentifiers[characters[0]].isNull()) + return m_shortIdentifiers[characters[0]]; + m_shortIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); + return m_shortIdentifiers[characters[0]]; + } + if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length)) + return m_recentIdentifiers[characters[0]]; + m_recentIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); + return m_recentIdentifiers[characters[0]]; +} + +template <typename CharType> +template <ParserMode mode> TokenType LiteralParser<CharType>::Lexer::lex(LiteralParserToken<CharType>& 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 TokLParen; + case ')': + token.type = TokRParen; + token.end = ++m_ptr; + return TokRParen; + 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 '"': + return lexString<mode, '"'>(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); + } + if (m_ptr < m_end) { + if (*m_ptr == '.') { + token.type = TokDot; + token.end = ++m_ptr; + return TokDot; + } + if (*m_ptr == '=') { + token.type = TokAssign; + token.end = ++m_ptr; + return TokAssign; + } + if (*m_ptr == ';') { + token.type = TokSemi; + token.end = ++m_ptr; + return TokAssign; + } + if (isASCIIAlpha(*m_ptr) || *m_ptr == '_' || *m_ptr == '$') + return lexIdentifier(token); + if (*m_ptr == '\'') { + if (mode == StrictJSON) { + m_lexErrorMessage = "Single quotes (\') are not allowed in JSON"; + return TokError; + } + return lexString<mode, '\''>(token); + } + } + m_lexErrorMessage = String::format("Unrecognized token '%c'", *m_ptr).impl(); + return TokError; +} + +template <> +ALWAYS_INLINE TokenType LiteralParser<LChar>::Lexer::lexIdentifier(LiteralParserToken<LChar>& token) +{ + while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$')) + m_ptr++; + token.stringIs8Bit = 1; + token.stringToken8 = token.start; + token.stringLength = m_ptr - token.start; + token.type = TokIdentifier; + token.end = m_ptr; + return TokIdentifier; +} + +template <> +ALWAYS_INLINE TokenType LiteralParser<UChar>::Lexer::lexIdentifier(LiteralParserToken<UChar>& token) +{ + while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$')) + m_ptr++; + token.stringIs8Bit = 0; + token.stringToken16 = token.start; + token.stringLength = m_ptr - token.start; + token.type = TokIdentifier; + token.end = m_ptr; + return TokIdentifier; +} + +template <typename CharType> +TokenType LiteralParser<CharType>::Lexer::next() +{ + if (m_mode == NonStrictJSON) + return lex<NonStrictJSON>(m_currentToken); + if (m_mode == JSONP) + return lex<JSONP>(m_currentToken); + return lex<StrictJSON>(m_currentToken); +} + +template <> +ALWAYS_INLINE void setParserTokenString<LChar>(LiteralParserToken<LChar>& token, const LChar* string) +{ + token.stringIs8Bit = 1; + token.stringToken8 = string; +} + +template <> +ALWAYS_INLINE void setParserTokenString<UChar>(LiteralParserToken<UChar>& token, const UChar* string) +{ + token.stringIs8Bit = 0; + token.stringToken16 = string; +} + +template <ParserMode mode, typename CharType, LChar terminator> static inline bool isSafeStringCharacter(LChar c) +{ + return (c >= ' ' && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON); +} + +template <ParserMode mode, typename CharType, UChar terminator> static inline bool isSafeStringCharacter(UChar c) +{ + return (c >= ' ' && (mode == StrictJSON || c <= 0xff) && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON); +} + +template <typename CharType> +template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType LiteralParser<CharType>::Lexer::lexString(LiteralParserToken<CharType>& token) +{ + ++m_ptr; + const CharType* runStart = m_ptr; + UStringBuilder builder; + do { + runStart = m_ptr; + while (m_ptr < m_end && isSafeStringCharacter<mode, CharType, terminator>(*m_ptr)) + ++m_ptr; + if (builder.length()) + builder.append(runStart, m_ptr - runStart); + if ((mode != NonStrictJSON) && m_ptr < m_end && *m_ptr == '\\') { + if (builder.isEmpty() && runStart < m_ptr) + builder.append(runStart, m_ptr - runStart); + ++m_ptr; + if (m_ptr >= m_end) { + m_lexErrorMessage = "Unterminated string"; + 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) { + m_lexErrorMessage = "\\u must be followed by 4 hex digits"; + return TokError; + } // uNNNN == 5 characters + for (int i = 1; i < 5; i++) { + if (!isASCIIHexDigit(m_ptr[i])) { + m_lexErrorMessage = String::format("\"\\%s\" is not a valid unicode escape", UString(m_ptr, 5).ascii().data()).impl(); + return TokError; + } + } + builder.append(JSC::Lexer<CharType>::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4])); + m_ptr += 5; + break; + + default: + if (*m_ptr == '\'' && mode != StrictJSON) { + builder.append('\''); + m_ptr++; + break; + } + m_lexErrorMessage = String::format("Invalid escape character %c", *m_ptr).impl(); + return TokError; + } + } + } while ((mode != NonStrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != terminator); + + if (m_ptr >= m_end || *m_ptr != terminator) { + m_lexErrorMessage = "Unterminated string"; + return TokError; + } + + if (builder.isEmpty()) { + token.stringBuffer = UString(); + setParserTokenString<CharType>(token, runStart); + token.stringLength = m_ptr - runStart; + } else { + token.stringBuffer = builder.toUString(); + if (token.stringBuffer.is8Bit()) { + token.stringIs8Bit = 1; + token.stringToken8 = token.stringBuffer.characters8(); + } else { + token.stringIs8Bit = 0; + token.stringToken16 = token.stringBuffer.characters16(); + } + token.stringLength = token.stringBuffer.length(); + } + token.type = TokString; + token.end = ++m_ptr; + return TokString; +} + +template <typename CharType> +TokenType LiteralParser<CharType>::Lexer::lexNumber(LiteralParserToken<CharType>& 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 { + m_lexErrorMessage = "Invalid number"; + return TokError; + } + + // ('.' [0-9]+)? + if (m_ptr < m_end && *m_ptr == '.') { + ++m_ptr; + // [0-9]+ + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) { + m_lexErrorMessage = "Invalid digits after decimal point"; + return TokError; + } + + ++m_ptr; + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) < 10) { + int result = 0; + token.type = TokNumber; + token.end = m_ptr; + const CharType* digit = token.start; + int negative = 1; + if (*digit == '-') { + negative = -1; + digit++; + } + + while (digit < m_ptr) + result = result * 10 + (*digit++) - '0'; + result *= negative; + token.numberToken = result; + return TokNumber; + } + + // ([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)) { + m_lexErrorMessage = "Exponent symbols should be followed by an optional '+' or '-' and then by at least one number"; + 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; +} + +template <typename CharType> +JSValue LiteralParser<CharType>::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) { + m_parseErrorMessage = "Unexpected comma at the end of array expression"; + 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) { + m_parseErrorMessage = "Expected ']'"; + 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 || (m_mode != StrictJSON && type == TokIdentifier)) { + LiteralParserToken<CharType> identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) { + m_parseErrorMessage = "Expected ':' before value in object property definition"; + return JSValue(); + } + + m_lexer.next(); + if (identifierToken.stringIs8Bit) + identifierStack.append(makeIdentifier(identifierToken.stringToken8, identifierToken.stringLength)); + else + identifierStack.append(makeIdentifier(identifierToken.stringToken16, identifierToken.stringLength)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } + if (type != TokRBrace) { + m_parseErrorMessage = "Expected '}'"; + return JSValue(); + } + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + doParseObjectStartExpression: + case DoParseObjectStartExpression: { + TokenType type = m_lexer.next(); + if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier)) { + m_parseErrorMessage = "Property name must be a string literal"; + return JSValue(); + } + LiteralParserToken<CharType> identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) { + m_parseErrorMessage = "Expected ':'"; + return JSValue(); + } + + m_lexer.next(); + if (identifierToken.stringIs8Bit) + identifierStack.append(makeIdentifier(identifierToken.stringToken8, identifierToken.stringLength)); + else + identifierStack.append(makeIdentifier(identifierToken.stringToken16, identifierToken.stringLength)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } + case DoParseObjectEndExpression: + { + asObject(objectStack.last())->putDirect(m_exec->globalData(), identifierStack.last(), lastValue); + identifierStack.removeLast(); + if (m_lexer.currentToken().type == TokComma) + goto doParseObjectStartExpression; + if (m_lexer.currentToken().type != TokRBrace) { + m_parseErrorMessage = "Expected '}'"; + 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: { + LiteralParserToken<CharType> stringToken = m_lexer.currentToken(); + m_lexer.next(); + if (stringToken.stringIs8Bit) + lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken8, stringToken.stringLength).ustring()); + else + lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken16, stringToken.stringLength).ustring()); + break; + } + case TokNumber: { + LiteralParserToken<CharType> 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; + case TokRBracket: + m_parseErrorMessage = "Unexpected token ']'"; + return JSValue(); + case TokRBrace: + m_parseErrorMessage = "Unexpected token '}'"; + return JSValue(); + case TokIdentifier: { + const LiteralParserToken<CharType>& token = m_lexer.currentToken(); + if (token.stringIs8Bit) + m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", UString(m_lexer.currentToken().stringToken8, m_lexer.currentToken().stringLength).ascii().data()).impl(); + else + m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", UString(m_lexer.currentToken().stringToken16, m_lexer.currentToken().stringLength).ascii().data()).impl(); + return JSValue(); + } + case TokColon: + m_parseErrorMessage = "Unexpected token ':'"; + return JSValue(); + case TokLParen: + m_parseErrorMessage = "Unexpected token '('"; + return JSValue(); + case TokRParen: + m_parseErrorMessage = "Unexpected token ')'"; + return JSValue(); + case TokComma: + m_parseErrorMessage = "Unexpected token ','"; + return JSValue(); + case TokDot: + m_parseErrorMessage = "Unexpected token '.'"; + return JSValue(); + case TokAssign: + m_parseErrorMessage = "Unexpected token '='"; + return JSValue(); + case TokSemi: + m_parseErrorMessage = "Unexpected token ';'"; + return JSValue(); + case TokEnd: + m_parseErrorMessage = "Unexpected EOF"; + return JSValue(); + case TokError: + default: + // Error + m_parseErrorMessage = "Could not parse value expression"; + 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; + } + case TokRBracket: + m_parseErrorMessage = "Unexpected token ']'"; + return JSValue(); + case TokLBrace: + m_parseErrorMessage = "Unexpected token '{'"; + return JSValue(); + case TokRBrace: + m_parseErrorMessage = "Unexpected token '}'"; + return JSValue(); + case TokIdentifier: + m_parseErrorMessage = "Unexpected identifier"; + return JSValue(); + case TokColon: + m_parseErrorMessage = "Unexpected token ':'"; + return JSValue(); + case TokRParen: + m_parseErrorMessage = "Unexpected token ')'"; + return JSValue(); + case TokComma: + m_parseErrorMessage = "Unexpected token ','"; + return JSValue(); + case TokTrue: + m_parseErrorMessage = "Unexpected token 'true'"; + return JSValue(); + case TokFalse: + m_parseErrorMessage = "Unexpected token 'false'"; + return JSValue(); + case TokNull: + m_parseErrorMessage = "Unexpected token 'null'"; + return JSValue(); + case TokEnd: + m_parseErrorMessage = "Unexpected EOF"; + return JSValue(); + case TokDot: + m_parseErrorMessage = "Unexpected token '.'"; + return JSValue(); + case TokAssign: + m_parseErrorMessage = "Unexpected token '='"; + return JSValue(); + case TokSemi: + m_parseErrorMessage = "Unexpected token ';'"; + return JSValue(); + case TokError: + default: + m_parseErrorMessage = "Could not parse statement"; + return JSValue(); + } + } + case StartParseStatementEndStatement: { + ASSERT(stateStack.isEmpty()); + if (m_lexer.currentToken().type != TokRParen) + return JSValue(); + if (m_lexer.next() == TokEnd) + return lastValue; + m_parseErrorMessage = "Unexpected content at end of JSON literal"; + return JSValue(); + } + default: + ASSERT_NOT_REACHED(); + } + if (stateStack.isEmpty()) + return lastValue; + state = stateStack.last(); + stateStack.removeLast(); + continue; + } +} + +// Instantiate the two flavors of LiteralParser we need instead of putting most of this file in LiteralParser.h +template class LiteralParser<LChar>; +template class LiteralParser<UChar>; + +} diff --git a/Source/JavaScriptCore/runtime/LiteralParser.h b/Source/JavaScriptCore/runtime/LiteralParser.h new file mode 100644 index 000000000..abe3f95b7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/LiteralParser.h @@ -0,0 +1,166 @@ +/* + * 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 "Identifier.h" +#include "JSGlobalObjectFunctions.h" +#include "JSValue.h" +#include "UString.h" + +namespace JSC { + +typedef enum { StrictJSON, NonStrictJSON, JSONP } ParserMode; + +enum JSONPPathEntryType { + JSONPPathEntryTypeDeclare, // var pathEntryName = JSON + JSONPPathEntryTypeDot, // <prior entries>.pathEntryName = JSON + JSONPPathEntryTypeLookup, // <prior entries>[pathIndex] = JSON + JSONPPathEntryTypeCall // <prior entries>(JSON) +}; + +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, TokDot, TokAssign, TokSemi, TokError }; + +struct JSONPPathEntry { + JSONPPathEntryType m_type; + Identifier m_pathEntryName; + int m_pathIndex; +}; + +struct JSONPData { + Vector<JSONPPathEntry> m_path; + Strong<Unknown> m_value; +}; + +template <typename CharType> +struct LiteralParserToken { + TokenType type; + const CharType* start; + const CharType* end; + UString stringBuffer; + union { + double numberToken; + struct { + union { + const LChar* stringToken8; + const UChar* stringToken16; + }; + unsigned stringIs8Bit : 1; + unsigned stringLength : 31; + }; + }; +}; + +template <typename CharType> +ALWAYS_INLINE void setParserTokenString(LiteralParserToken<CharType>&, const CharType* string); + +template <typename CharType> +class LiteralParser { +public: + LiteralParser(ExecState* exec, const CharType* characters, unsigned length, ParserMode mode) + : m_exec(exec) + , m_lexer(characters, length, mode) + , m_mode(mode) + { + } + + UString getErrorMessage() + { + if (!m_lexer.getErrorMessage().isEmpty()) + return String::format("JSON Parse error: %s", m_lexer.getErrorMessage().ascii().data()).impl(); + if (!m_parseErrorMessage.isEmpty()) + return String::format("JSON Parse error: %s", m_parseErrorMessage.ascii().data()).impl(); + return "JSON Parse error: Unable to parse JSON string"; + } + + JSValue tryLiteralParse() + { + m_lexer.next(); + JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement); + if (m_lexer.currentToken().type == TokSemi) + m_lexer.next(); + if (m_lexer.currentToken().type != TokEnd) + return JSValue(); + return result; + } + + bool tryJSONPParse(Vector<JSONPData>&, bool needsFullSourceInfo); + +private: + class Lexer { + public: + Lexer(const CharType* characters, unsigned length, ParserMode mode) + : m_mode(mode) + , m_ptr(characters) + , m_end(characters + length) + { + } + + TokenType next(); + + const LiteralParserToken<CharType>& currentToken() + { + return m_currentToken; + } + + UString getErrorMessage() { return m_lexErrorMessage; } + + private: + UString m_lexErrorMessage; + template <ParserMode mode> TokenType lex(LiteralParserToken<CharType>&); + ALWAYS_INLINE TokenType lexIdentifier(LiteralParserToken<CharType>&); + template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType lexString(LiteralParserToken<CharType>&); + ALWAYS_INLINE TokenType lexNumber(LiteralParserToken<CharType>&); + LiteralParserToken<CharType> m_currentToken; + ParserMode m_mode; + const CharType* m_ptr; + const CharType* m_end; + }; + + class StackGuard; + JSValue parse(ParserState); + + ExecState* m_exec; + typename LiteralParser<CharType>::Lexer m_lexer; + ParserMode m_mode; + UString m_parseErrorMessage; + static unsigned const MaximumCachableCharacter = 128; + FixedArray<Identifier, MaximumCachableCharacter> m_shortIdentifiers; + FixedArray<Identifier, MaximumCachableCharacter> m_recentIdentifiers; + ALWAYS_INLINE const Identifier makeIdentifier(const LChar* characters, size_t length); + ALWAYS_INLINE const Identifier makeIdentifier(const UChar* characters, size_t length); + }; + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/Lookup.cpp b/Source/JavaScriptCore/runtime/Lookup.cpp new file mode 100644 index 000000000..55c048fa3 --- /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" + +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, values[i].intrinsic); + } + 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; + } +} + +bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) +{ + ASSERT(thisObj->globalObject()); + ASSERT(entry->attributes() & Function); + WriteBarrierBase<Unknown>* location = thisObj->getDirectLocation(exec->globalData(), propertyName); + + if (!location) { + // If a property is ever deleted from an object with a static table, then we reify + // all static functions at that time - after this we shouldn't be re-adding anything. + if (thisObj->staticFunctionsReified()) + return false; + + JSFunction* function; + JSGlobalObject* globalObject = thisObj->globalObject(); +#if ENABLE(JIT) + if (exec->globalData().canUseJIT() && entry->intrinsic() != NoIntrinsic) + function = JSFunction::create(exec, globalObject, entry->functionLength(), propertyName, exec->globalData().getHostFunction(entry->function(), entry->intrinsic())); + else +#endif + function = JSFunction::create(exec, globalObject, entry->functionLength(), propertyName, entry->function()); + + thisObj->putDirect(exec->globalData(), propertyName, function, entry->attributes()); + location = thisObj->getDirectLocation(exec->globalData(), propertyName); + } + + slot.setValue(thisObj, location->get(), thisObj->offsetForLocation(location)); + return true; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h new file mode 100644 index 000000000..8ed70b41d --- /dev/null +++ b/Source/JavaScriptCore/runtime/Lookup.h @@ -0,0 +1,384 @@ +/* + * 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 "Intrinsic.h" +#include "Identifier.h" +#include "JSGlobalObject.h" +#include "PropertySlot.h" +#include <stdio.h> +#include <wtf/Assertions.h> + +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; + Intrinsic intrinsic; + }; + + // 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 { + WTF_MAKE_FAST_ALLOCATED; + public: + void initialize(StringImpl* key, unsigned char attributes, intptr_t v1, intptr_t v2, Intrinsic intrinsic) + { + m_key = key; + m_attributes = attributes; + m_u.store.value1 = v1; + m_u.store.value2 = v2; + m_u.function.intrinsic = intrinsic; + m_next = 0; + } + + void setKey(StringImpl* key) { m_key = key; } + StringImpl* key() const { return m_key; } + + unsigned char attributes() const { return m_attributes; } + + Intrinsic intrinsic() const + { + ASSERT(m_attributes & Function); + return m_u.function.intrinsic; + } + + 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 + Intrinsic intrinsic; + } 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); + } + + class ConstIterator { + public: + ConstIterator(const HashTable* table, int position) + : m_table(table) + , m_position(position) + { + skipInvalidKeys(); + } + + const HashEntry* operator->() + { + return &m_table->table[m_position]; + } + + const HashEntry* operator*() + { + return &m_table->table[m_position]; + } + + bool operator!=(const ConstIterator& other) + { + ASSERT(m_table == other.m_table); + return m_position != other.m_position; + } + + ConstIterator& operator++() + { + ASSERT(m_position < m_table->compactSize); + ++m_position; + skipInvalidKeys(); + return *this; + } + + private: + void skipInvalidKeys() + { + ASSERT(m_position <= m_table->compactSize); + while (m_position < m_table->compactSize && !m_table->table[m_position].key()) + ++m_position; + ASSERT(m_position <= m_table->compactSize); + } + + const HashTable* m_table; + int m_position; + }; + + ConstIterator begin(JSGlobalData& globalData) const + { + initializeIfNeeded(&globalData); + return ConstIterator(this, 0); + } + ConstIterator end(JSGlobalData& globalData) const + { + initializeIfNeeded(&globalData); + return ConstIterator(this, compactSize); + } + + 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; + }; + + bool 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 ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + + if (entry->attributes() & Function) + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + + 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 ParentImp::getOwnPropertyDescriptor(thisObj, exec, propertyName, descriptor); + + PropertySlot slot; + if (entry->attributes() & Function) { + bool present = setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + if (present) + descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); + return present; + } + + 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 (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) + return true; + + const HashEntry* entry = table->entry(exec, propertyName); + if (!entry) + return false; + + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + } + + /** + * 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 (ParentImp::getOwnPropertyDescriptor(static_cast<ParentImp*>(thisObj), exec, propertyName, descriptor)) + return true; + + const HashEntry* entry = table->entry(exec, propertyName); + if (!entry) + return false; + + PropertySlot slot; + bool present = setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + if (present) + descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); + return present; + } + + /** + * 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 ParentImp::getOwnPropertySlot(thisObj, 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 ParentImp::getOwnPropertyDescriptor(thisObj, 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, bool shouldThrow = false) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) + return false; + + // If this is a function put it as an override property. + if (entry->attributes() & Function) + thisObj->putDirect(exec->globalData(), propertyName, value); + else if (!(entry->attributes() & ReadOnly)) + entry->propertyPutter()(exec, thisObj, value); + else if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + + 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, slot.isStrictMode())) + ParentImp::put(thisObj, exec, propertyName, value, slot); // not found: forward to parent + } + +} // namespace JSC + +#endif // Lookup_h diff --git a/Source/JavaScriptCore/runtime/MathObject.cpp b/Source/JavaScriptCore/runtime/MathObject.cpp new file mode 100644 index 000000000..58b09122d --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathObject.cpp @@ -0,0 +1,246 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(MathObject); + +const ClassInfo MathObject::s_info = { "Math", &JSNonFinalObject::s_info, 0, ExecState::mathTable, CREATE_METHOD_TABLE(MathObject) }; + +/* 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(JSGlobalObject* globalObject, Structure* structure) + : JSNonFinalObject(globalObject->globalData(), structure) +{ +} + +void MathObject::finishCreation(ExecState* exec, JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalData()); + ASSERT(inherits(&s_info)); + + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "E"), jsNumber(exp(1.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "LN2"), jsNumber(log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "LN10"), jsNumber(log(10.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "LOG2E"), jsNumber(1.0 / log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "LOG10E"), jsNumber(0.4342944819032518), DontDelete | DontEnum | ReadOnly); // See ECMA-262 15.8.1.5 + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "PI"), jsNumber(piDouble), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "SQRT1_2"), jsNumber(sqrt(0.5)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(exec->globalData(), Identifier(exec, "SQRT2"), jsNumber(sqrt(2.0)), DontDelete | DontEnum | ReadOnly); +} + +bool MathObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<JSObject>(exec, ExecState::mathTable(exec), jsCast<MathObject*>(cell), propertyName, slot); +} + +bool MathObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::mathTable(exec), jsCast<MathObject*>(object), 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 = -std::numeric_limits<double>::infinity(); + for (unsigned k = 0; k < argsCount; ++k) { + double val = exec->argument(k).toNumber(exec); + if (isnan(val)) { + result = std::numeric_limits<double>::quiet_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 = +std::numeric_limits<double>::infinity(); + for (unsigned k = 0; k < argsCount; ++k) { + double val = exec->argument(k).toNumber(exec); + if (isnan(val)) { + result = std::numeric_limits<double>::quiet_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 000000000..d8da039d8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathObject.h @@ -0,0 +1,58 @@ +/* + * 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 "JSObject.h" + +namespace JSC { + + class MathObject : public JSNonFinalObject { + private: + MathObject(JSGlobalObject*, Structure*); + + public: + typedef JSNonFinalObject Base; + + static MathObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + MathObject* object = new (NotNull, allocateCell<MathObject>(*exec->heap())) MathObject(globalObject, structure); + object->finishCreation(exec, globalObject); + return object; + } + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, JSGlobalObject*); + 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 000000000..86101f559 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp @@ -0,0 +1,50 @@ +/* + * 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 { + +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 000000000..d4b8b6fc1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.h @@ -0,0 +1,45 @@ +/* + * 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 "Heap.h" + +class JSGlobalData; + +namespace JSC { + +struct GlobalMemoryStatistics { + size_t stackBytes; + size_t JITBytes; +}; + +GlobalMemoryStatistics globalMemoryStatistics(); + +} + +#endif // MemoryStatistics_h + diff --git a/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp new file mode 100644 index 000000000..b6aff916e --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.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 "NativeErrorConstructor.h" + +#include "ErrorInstance.h" +#include "JSFunction.h" +#include "JSString.h" +#include "NativeErrorPrototype.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NativeErrorConstructor); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(NativeErrorConstructor); + +const ClassInfo NativeErrorConstructor::s_info = { "Function", &InternalFunction::s_info, 0, 0, CREATE_METHOD_TABLE(NativeErrorConstructor) }; + +NativeErrorConstructor::NativeErrorConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void NativeErrorConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + NativeErrorConstructor* thisObject = jsCast<NativeErrorConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + InternalFunction::visitChildren(thisObject, visitor); + if (thisObject->m_errorStructure) + visitor.append(&thisObject->m_errorStructure); +} + +static EncodedJSValue JSC_HOST_CALL constructWithNativeErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + ASSERT(errorStructure); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); +} + +ConstructType NativeErrorConstructor::getConstructData(JSCell*, 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(JSCell*, 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 000000000..41e4235fa --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.h @@ -0,0 +1,80 @@ +/* + * 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" +#include "NativeErrorPrototype.h" + +namespace JSC { + + class ErrorInstance; + class FunctionPrototype; + class NativeErrorPrototype; + + class NativeErrorConstructor : public InternalFunction { + public: + typedef InternalFunction Base; + + static NativeErrorConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, Structure* prototypeStructure, const UString& name) + { + NativeErrorConstructor* constructor = new (NotNull, allocateCell<NativeErrorConstructor>(*exec->heap())) NativeErrorConstructor(globalObject, structure); + constructor->finishCreation(exec, globalObject, prototypeStructure, name); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + Structure* errorStructure() { return m_errorStructure.get(); } + + protected: + void finishCreation(ExecState* exec, JSGlobalObject* globalObject, Structure* prototypeStructure, const UString& name) + { + Base::finishCreation(exec->globalData(), Identifier(exec, name)); + ASSERT(inherits(&s_info)); + + NativeErrorPrototype* prototype = NativeErrorPrototype::create(exec, globalObject, prototypeStructure, name, this); + + putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 + putDirect(exec->globalData(), exec->propertyNames().prototype, prototype, DontDelete | ReadOnly | DontEnum); + m_errorStructure.set(exec->globalData(), this, ErrorInstance::createStructure(exec->globalData(), globalObject, prototype)); + ASSERT(m_errorStructure); + ASSERT(m_errorStructure->isObject()); + } + + private: + NativeErrorConstructor(JSGlobalObject*, Structure*); + static const unsigned StructureFlags = OverridesVisitChildren | InternalFunction::StructureFlags; + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<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 000000000..dfdd87f85 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp @@ -0,0 +1,46 @@ +/* + * 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 "JSGlobalObject.h" +#include "JSString.h" +#include "NativeErrorConstructor.h" +#include "UString.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(NativeErrorPrototype); + +NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, Structure* structure) + : ErrorPrototype(exec, structure) +{ +} + +void NativeErrorPrototype::finishCreation(ExecState* exec, JSGlobalObject* globalObject, const UString& nameAndMessage, NativeErrorConstructor* constructor) +{ + Base::finishCreation(exec, globalObject); + putDirect(exec->globalData(), exec->propertyNames().name, jsString(exec, nameAndMessage), DontEnum); + putDirect(exec->globalData(), exec->propertyNames().message, jsEmptyString(exec), DontEnum); + putDirect(exec->globalData(), 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 000000000..4bceb883a --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.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 NativeErrorPrototype_h +#define NativeErrorPrototype_h + +#include "ErrorPrototype.h" + +namespace JSC { + class NativeErrorConstructor; + + class NativeErrorPrototype : public ErrorPrototype { + private: + NativeErrorPrototype(ExecState*, Structure*); + + public: + typedef ErrorPrototype Base; + + static NativeErrorPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, const UString& name, NativeErrorConstructor* constructor) + { + NativeErrorPrototype* prototype = new (NotNull, allocateCell<NativeErrorPrototype>(*exec->heap())) NativeErrorPrototype(exec, structure); + prototype->finishCreation(exec, globalObject, name, constructor); + return prototype; + } + + protected: + void finishCreation(ExecState*, JSGlobalObject*, const UString& nameAndMessage, NativeErrorConstructor*); + }; + +} // namespace JSC + +#endif // NativeErrorPrototype_h diff --git a/Source/JavaScriptCore/runtime/NumberConstructor.cpp b/Source/JavaScriptCore/runtime/NumberConstructor.cpp new file mode 100644 index 000000000..e1ff62f32 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberConstructor.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2011 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(NumberConstructor); + +const ClassInfo NumberConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::numberConstructorTable, CREATE_METHOD_TABLE(NumberConstructor) }; + +/* Source for NumberConstructor.lut.h +@begin numberConstructorTable + 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(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void NumberConstructor::finishCreation(ExecState* exec, NumberPrototype* numberPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, numberPrototype->s_info.className)); + ASSERT(inherits(&s_info)); + + // Number.Prototype + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, numberPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool NumberConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<NumberConstructor, InternalFunction>(exec, ExecState::numberConstructorTable(exec), jsCast<NumberConstructor*>(cell), propertyName, slot); +} + +bool NumberConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticValueDescriptor<NumberConstructor, InternalFunction>(exec, ExecState::numberConstructorTable(exec), jsCast<NumberConstructor*>(object), propertyName, descriptor); +} + +void NumberConstructor::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + lookupPut<NumberConstructor, InternalFunction>(exec, propertyName, value, ExecState::numberConstructorTable(exec), jsCast<NumberConstructor*>(cell), slot); +} + +static JSValue numberConstructorNaNValue(ExecState*, JSValue, const Identifier&) +{ + return jsNaN(); +} + +static JSValue numberConstructorNegInfinity(ExecState*, JSValue, const Identifier&) +{ + return jsNumber(-std::numeric_limits<double>::infinity()); +} + +static JSValue numberConstructorPosInfinity(ExecState*, JSValue, const Identifier&) +{ + return jsNumber(std::numeric_limits<double>::infinity()); +} + +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 = NumberObject::create(exec->globalData(), asInternalFunction(exec->callee())->globalObject()->numberObjectStructure()); + double n = exec->argumentCount() ? exec->argument(0).toNumber(exec) : 0; + object->setInternalValue(exec->globalData(), jsNumber(n)); + return JSValue::encode(object); +} + +ConstructType NumberConstructor::getConstructData(JSCell*, 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(JSCell*, 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 000000000..4ee5b0716 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberConstructor.h @@ -0,0 +1,68 @@ +/* + * 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: + typedef InternalFunction Base; + + static NumberConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, NumberPrototype* numberPrototype) + { + NumberConstructor* constructor = new (NotNull, allocateCell<NumberConstructor>(*exec->heap())) NumberConstructor(globalObject, structure); + constructor->finishCreation(exec, numberPrototype); + return constructor; + } + + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + JSValue getValueProperty(ExecState*, int token) const; + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(globalData, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + enum { NaNValue, NegInfinity, PosInfinity, MaxValue, MinValue }; + + protected: + void finishCreation(ExecState*, NumberPrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | InternalFunction::StructureFlags; + + private: + NumberConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + }; + +} // namespace JSC + +#endif // NumberConstructor_h diff --git a/Source/JavaScriptCore/runtime/NumberObject.cpp b/Source/JavaScriptCore/runtime/NumberObject.cpp new file mode 100644 index 000000000..1fea25464 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberObject.cpp @@ -0,0 +1,53 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(NumberObject); + +const ClassInfo NumberObject::s_info = { "Number", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(NumberObject) }; + +NumberObject::NumberObject(JSGlobalData& globalData, Structure* structure) + : JSWrapperObject(globalData, structure) +{ +} + +void NumberObject::finishCreation(JSGlobalData& globalData) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); +} + +NumberObject* constructNumber(ExecState* exec, JSGlobalObject* globalObject, JSValue number) +{ + NumberObject* object = NumberObject::create(exec->globalData(), globalObject->numberObjectStructure()); + object->setInternalValue(exec->globalData(), number); + return object; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberObject.h b/Source/JavaScriptCore/runtime/NumberObject.h new file mode 100644 index 000000000..07334722b --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberObject.h @@ -0,0 +1,55 @@ +/* + * 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 { + protected: + NumberObject(JSGlobalData&, Structure*); + void finishCreation(JSGlobalData&); + + public: + typedef JSWrapperObject Base; + + static NumberObject* create(JSGlobalData& globalData, Structure* structure) + { + NumberObject* number = new (NotNull, allocateCell<NumberObject>(globalData.heap)) NumberObject(globalData, structure); + number->finishCreation(globalData); + return number; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(NumberObjectType, StructureFlags), &s_info); + } + }; + + NumberObject* constructNumber(ExecState*, JSGlobalObject*, 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 000000000..4612b56d2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberPrototype.cpp @@ -0,0 +1,488 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2011 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 "BigInteger.h" +#include "Error.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Operations.h" +#include "Uint16WithFraction.h" +#include "dtoa.h" +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> +#include <wtf/dtoa/double-conversion.h> + +using namespace WTF::double_conversion; + +// To avoid conflict with WTF::StringBuilder. +typedef WTF::double_conversion::StringBuilder DoubleConversionStringBuilder; + +namespace JSC { + +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*); + +} + +#include "NumberPrototype.lut.h" + +namespace JSC { + +const ClassInfo NumberPrototype::s_info = { "Number", &NumberObject::s_info, 0, ExecState::numberPrototypeTable, CREATE_METHOD_TABLE(NumberPrototype) }; + +/* Source for NumberPrototype.lut.h +@begin numberPrototypeTable + toString numberProtoFuncToString DontEnum|Function 1 + toLocaleString numberProtoFuncToLocaleString DontEnum|Function 0 + valueOf numberProtoFuncValueOf DontEnum|Function 0 + toFixed numberProtoFuncToFixed DontEnum|Function 1 + toExponential numberProtoFuncToExponential DontEnum|Function 1 + toPrecision numberProtoFuncToPrecision DontEnum|Function 1 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(NumberPrototype); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(NumberPrototype); + +NumberPrototype::NumberPrototype(ExecState* exec, Structure* structure) + : NumberObject(exec->globalData(), structure) +{ +} + +void NumberPrototype::finishCreation(ExecState* exec, JSGlobalObject*) +{ + Base::finishCreation(exec->globalData()); + setInternalValue(exec->globalData(), jsNumber(0)); + + ASSERT(inherits(&s_info)); +} + +bool NumberPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<NumberObject>(exec, ExecState::numberPrototypeTable(exec), jsCast<NumberPrototype*>(cell), propertyName, slot); +} + +bool NumberPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<NumberObject>(exec, ExecState::numberPrototypeTable(exec), jsCast<NumberPrototype*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions --------------------------- + +static ALWAYS_INLINE bool toThisNumber(JSValue thisValue, double& x) +{ + if (thisValue.isInt32()) { + x = thisValue.asInt32(); + return true; + } + + if (thisValue.isDouble()) { + x = thisValue.asDouble(); + return true; + } + + if (thisValue.isCell() && thisValue.asCell()->structure()->typeInfo().isNumberObject()) { + x = static_cast<const NumberObject*>(thisValue.asCell())->internalValue().asNumber(); + return true; + } + + return false; +} + +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; +} + +// The largest finite floating point number is 1.mantissa * 2^(0x7fe-0x3ff). +// Since 2^N in binary is a one bit followed by N zero bits. 1 * 2^3ff requires +// at most 1024 characters to the left of a decimal point, in base 2 (1025 if +// we include a minus sign). For the fraction, a value with an exponent of 0 +// has up to 52 bits to the right of the decimal point. Each decrement of the +// exponent down to a minimum of -0x3fe adds an additional digit to the length +// of the fraction. As such the maximum fraction size is 1075 (1076 including +// a point). We pick a buffer size such that can simply place the point in the +// center of the buffer, and are guaranteed to have enough space in each direction +// fo any number of digits an IEEE number may require to represent. +typedef char RadixBuffer[2180]; + +// Mapping from integers 0..35 to digit identifying this value, for radix 2..36. +static const char* const radixDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; + +static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix) +{ + ASSERT(isfinite(number)); + ASSERT(radix >= 2 && radix <= 36); + + // Position the decimal point at the center of the string, set + // the startOfResultString pointer to point at the decimal point. + char* decimalPoint = buffer + sizeof(buffer) / 2; + char* startOfResultString = decimalPoint; + + // Extract the sign. + bool isNegative = number < 0; + if (signbit(number)) + number = -number; + double integerPart = floor(number); + + // We use this to test for odd values in odd radix bases. + // Where the base is even, (e.g. 10), to determine whether a value is even we need only + // consider the least significant digit. For example, 124 in base 10 is even, because '4' + // is even. if the radix is odd, then the radix raised to an integer power is also odd. + // E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value + // is multiplied by an odd number, the result is even if the sum of all digits is even. + // + // For the integer portion of the result, we only need test whether the integer value is + // even or odd. For each digit of the fraction added, we should invert our idea of whether + // the number is odd if the new digit is odd. + // + // Also initialize digit to this value; for even radix values we only need track whether + // the last individual digit was odd. + bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1; + ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2))); + bool isOddInOddRadix = integerPartIsOdd; + uint32_t digit = integerPartIsOdd; + + // Check if the value has a fractional part to convert. + double fractionPart = number - integerPart; + if (fractionPart) { + // Write the decimal point now. + *decimalPoint = '.'; + + // Higher precision representation of the fractional part. + Uint16WithFraction fraction(fractionPart); + + bool needsRoundingUp = false; + char* endOfResultString = decimalPoint + 1; + + // Calculate the delta from the current number to the next & previous possible IEEE numbers. + double nextNumber = nextafter(number, std::numeric_limits<double>::infinity()); + double lastNumber = nextafter(number, -std::numeric_limits<double>::infinity()); + ASSERT(isfinite(nextNumber) && !signbit(nextNumber)); + ASSERT(isfinite(lastNumber) && !signbit(lastNumber)); + double deltaNextDouble = nextNumber - number; + double deltaLastDouble = number - lastNumber; + ASSERT(isfinite(deltaNextDouble) && !signbit(deltaNextDouble)); + ASSERT(isfinite(deltaLastDouble) && !signbit(deltaLastDouble)); + + // We track the delta from the current value to the next, to track how many digits of the + // fraction we need to write. For example, if the value we are converting is precisely + // 1.2345, so far we have written the digits "1.23" to a string leaving a remainder of + // 0.45, and we want to determine whether we can round off, or whether we need to keep + // appending digits ('4'). We can stop adding digits provided that then next possible + // lower IEEE value is further from 1.23 than the remainder we'd be rounding off (0.45), + // which is to say, less than 1.2255. Put another way, the delta between the prior + // possible value and this number must be more than 2x the remainder we'd be rounding off + // (or more simply half the delta between numbers must be greater than the remainder). + // + // Similarly we need track the delta to the next possible value, to dertermine whether + // to round up. In almost all cases (other than at exponent boundaries) the deltas to + // prior and subsequent values are identical, so we don't need track then separately. + if (deltaNextDouble != deltaLastDouble) { + // Since the deltas are different track them separately. Pre-multiply by 0.5. + Uint16WithFraction halfDeltaNext(deltaNextDouble, 1); + Uint16WithFraction halfDeltaLast(deltaLastDouble, 1); + + while (true) { + // examine the remainder to determine whether we should be considering rounding + // up or down. If remainder is precisely 0.5 rounding is to even. + int dComparePoint5 = fraction.comparePoint5(); + if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) { + // Check for rounding up; are we closer to the value we'd round off to than + // the next IEEE value would be? + if (fraction.sumGreaterThanOne(halfDeltaNext)) { + needsRoundingUp = true; + break; + } + } else { + // Check for rounding down; are we closer to the value we'd round off to than + // the prior IEEE value would be? + if (fraction < halfDeltaLast) + break; + } + + ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1)); + // Write a digit to the string. + fraction *= radix; + digit = fraction.floorAndSubtract(); + *endOfResultString++ = radixDigits[digit]; + // Keep track whether the portion written is currently even, if the radix is odd. + if (digit & 1) + isOddInOddRadix = !isOddInOddRadix; + + // Shift the fractions by radix. + halfDeltaNext *= radix; + halfDeltaLast *= radix; + } + } else { + // This code is identical to that above, except since deltaNextDouble != deltaLastDouble + // we don't need to track these two values separately. + Uint16WithFraction halfDelta(deltaNextDouble, 1); + + while (true) { + int dComparePoint5 = fraction.comparePoint5(); + if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) { + if (fraction.sumGreaterThanOne(halfDelta)) { + needsRoundingUp = true; + break; + } + } else if (fraction < halfDelta) + break; + + ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1)); + fraction *= radix; + digit = fraction.floorAndSubtract(); + if (digit & 1) + isOddInOddRadix = !isOddInOddRadix; + *endOfResultString++ = radixDigits[digit]; + + halfDelta *= radix; + } + } + + // Check if the fraction needs rounding off (flag set in the loop writing digits, above). + if (needsRoundingUp) { + // Whilst the last digit is the maximum in the current radix, remove it. + // e.g. rounding up the last digit in "12.3999" is the same as rounding up the + // last digit in "12.3" - both round up to "12.4". + while (endOfResultString[-1] == radixDigits[radix - 1]) + --endOfResultString; + + // Radix digits are sequential in ascii/unicode, except for '9' and 'a'. + // E.g. the first 'if' case handles rounding 67.89 to 67.8a in base 16. + // The 'else if' case handles rounding of all other digits. + if (endOfResultString[-1] == '9') + endOfResultString[-1] = 'a'; + else if (endOfResultString[-1] != '.') + ++endOfResultString[-1]; + else { + // One other possibility - there may be no digits to round up in the fraction + // (or all may be been rounded off already), in which case we may need to + // round into the integer portion of the number. Remove the decimal point. + --endOfResultString; + // In order to get here there must have been a non-zero fraction, in which case + // there must be at least one bit of the value's mantissa not in use in the + // integer part of the number. As such, adding to the integer part should not + // be able to lose precision. + ASSERT((integerPart + 1) - integerPart == 1); + ++integerPart; + } + } else { + // We only need to check for trailing zeros if the value does not get rounded up. + while (endOfResultString[-1] == '0') + --endOfResultString; + } + + *endOfResultString = '\0'; + ASSERT(endOfResultString < buffer + sizeof(buffer)); + } else + *decimalPoint = '\0'; + + BigInteger units(integerPart); + + // Always loop at least once, to emit at least '0'. + do { + ASSERT(buffer < startOfResultString); + + // Read a single digit and write it to the front of the string. + // Divide by radix to remove one digit from the value. + digit = units.divide(radix); + *--startOfResultString = radixDigits[digit]; + } while (!!units); + + // If the number is negative, prepend '-'. + if (isNegative) + *--startOfResultString = '-'; + ASSERT(buffer <= startOfResultString); + + return startOfResultString; +} + +// 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) +{ + 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 (!isfinite(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Round if the argument is not undefined, always format as exponential. + char buffer[WTF::NumberToStringBufferLength]; + DoubleConversionStringBuilder builder(buffer, WTF::NumberToStringBufferLength); + const DoubleToStringConverter& converter = DoubleToStringConverter::EcmaScriptConverter(); + builder.Reset(); + isUndefined + ? converter.ToExponential(x, -1, &builder) + : converter.ToExponential(x, decimalPlacesInExponent, &builder); + return JSValue::encode(jsString(exec, UString(builder.Finalize()))); +} + +// 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) +{ + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + return throwVMTypeError(exec); + + // 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(isfinite(x)); + + NumberToStringBuffer buffer; + return JSValue::encode(jsString(exec, UString(numberToFixedWidthString(x, decimalPlaces, buffer)))); +} + +// 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) +{ + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + return throwVMTypeError(exec); + + // 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 (!isfinite(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + NumberToStringBuffer buffer; + return JSValue::encode(jsString(exec, UString(numberToFixedPrecisionString(x, significantFigures, buffer)))); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + 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, jsNumber(x).toString(exec))); + + // Fast path for number to character conversion. + if (radix == 36) { + unsigned c = static_cast<unsigned>(x); + if (c == x && c < 36) { + JSGlobalData* globalData = &exec->globalData(); + return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, radixDigits[c])); + } + } + + if (radix < 2 || radix > 36) + return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36")); + + if (!isfinite(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + RadixBuffer s; + return JSValue::encode(jsString(exec, toStringWithRadix(s, x, radix))); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + return throwVMTypeError(exec); + + return JSValue::encode(jsString(exec, jsNumber(x).toString(exec))); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + return throwVMTypeError(exec); + return JSValue::encode(jsNumber(x)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.h b/Source/JavaScriptCore/runtime/NumberPrototype.h new file mode 100644 index 000000000..d63cc3a6f --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberPrototype.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 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: + typedef NumberObject Base; + + static NumberPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + NumberPrototype* prototype = new (NotNull, allocateCell<NumberPrototype>(*exec->heap())) NumberPrototype(exec, structure); + prototype->finishCreation(exec, globalObject); + return prototype; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(NumberObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, JSGlobalObject*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | NumberObject::StructureFlags; + + private: + NumberPrototype(ExecState*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + }; + +} // namespace JSC + +#endif // NumberPrototype_h diff --git a/Source/JavaScriptCore/runtime/NumericStrings.h b/Source/JavaScriptCore/runtime/NumericStrings.h new file mode 100644 index 000000000..d65f14265 --- /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 000000000..65d28c18c --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -0,0 +1,415 @@ +/* + * 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 "Lookup.h" +#include "ObjectPrototype.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.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*); +static EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*); + +} + +#include "ObjectConstructor.lut.h" + +namespace JSC { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ObjectConstructor); + +const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::objectConstructorTable, CREATE_METHOD_TABLE(ObjectConstructor) }; + +/* Source for ObjectConstructor.lut.h +@begin objectConstructorTable + getPrototypeOf objectConstructorGetPrototypeOf DontEnum|Function 1 + getOwnPropertyDescriptor objectConstructorGetOwnPropertyDescriptor DontEnum|Function 2 + getOwnPropertyNames objectConstructorGetOwnPropertyNames DontEnum|Function 1 + keys objectConstructorKeys DontEnum|Function 1 + defineProperty objectConstructorDefineProperty DontEnum|Function 3 + defineProperties objectConstructorDefineProperties DontEnum|Function 2 + create objectConstructorCreate DontEnum|Function 2 + seal objectConstructorSeal DontEnum|Function 1 + freeze objectConstructorFreeze DontEnum|Function 1 + preventExtensions objectConstructorPreventExtensions DontEnum|Function 1 + isSealed objectConstructorIsSealed DontEnum|Function 1 + isFrozen objectConstructorIsFrozen DontEnum|Function 1 + isExtensible objectConstructorIsExtensible DontEnum|Function 1 +@end +*/ + +ObjectConstructor::ObjectConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void ObjectConstructor::finishCreation(ExecState* exec, ObjectPrototype* objectPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, "Object")); + // ECMA 15.2.3.1 + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); + // no. of arguments for constructor + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool ObjectConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<JSObject>(exec, ExecState::objectConstructorTable(exec), jsCast<ObjectConstructor*>(cell), propertyName, slot); +} + +bool ObjectConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSObject>(exec, ExecState::objectConstructorTable(exec), jsCast<ObjectConstructor*>(object), propertyName, descriptor); +} + +// ECMA 15.2.2 +static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args) +{ + JSValue arg = args.at(0); + if (arg.isUndefinedOrNull()) + return constructEmptyObject(exec, globalObject); + return arg.toObject(exec, globalObject); +} + +static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructObject(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +ConstructType ObjectConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithObjectConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructObject(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +CallType ObjectConstructor::getCallData(JSCell*, 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.")); + + // This uses JSValue::get() instead of directly accessing the prototype from the object + // (using JSObject::prototype()) in order to allow objects to override the behavior, such + // as returning jsUndefined() for cross-origin access. + return JSValue::encode(exec->argument(0).get(exec, exec->propertyNames().underscoreProto)); +} + +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->methodTable()->getOwnPropertyDescriptor(object, 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->globalData(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); + description->putDirect(exec->globalData(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); + } else { + description->putDirect(exec->globalData(), exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); + description->putDirect(exec->globalData(), exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); + } + + description->putDirect(exec->globalData(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); + description->putDirect(exec->globalData(), 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))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), 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))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, ExcludeDontEnumProperties); + 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->methodTable()->defineOwnProperty(O, exec, Identifier(exec, propertyName), descriptor, true); + return JSValue::encode(O); +} + +static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) +{ + PropertyNameArray propertyNames(exec); + asObject(properties)->methodTable()->getOwnPropertyNames(asObject(properties), exec, propertyNames, ExcludeDontEnumProperties); + 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->methodTable()->defineOwnProperty(object, 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.")); + JSValue proto = exec->argument(0); + JSObject* newObject = proto.isObject() ? constructEmptyObject(exec, asObject(proto)->inheritorID(exec->globalData())) : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure()); + 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)))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return throwVMError(exec, createTypeError(exec, "Object.seal can only be called on Objects.")); + asObject(obj)->seal(exec->globalData()); + return JSValue::encode(obj); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return throwVMError(exec, createTypeError(exec, "Object.freeze can only be called on Objects.")); + asObject(obj)->freeze(exec->globalData()); + return JSValue::encode(obj); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return throwVMError(exec, createTypeError(exec, "Object.preventExtensions can only be called on Objects.")); + asObject(obj)->preventExtensions(exec->globalData()); + return JSValue::encode(obj); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return throwVMError(exec, createTypeError(exec, "Object.isSealed can only be called on Objects.")); + return JSValue::encode(jsBoolean(asObject(obj)->isSealed(exec->globalData()))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return throwVMError(exec, createTypeError(exec, "Object.isFrozen can only be called on Objects.")); + return JSValue::encode(jsBoolean(asObject(obj)->isFrozen(exec->globalData()))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return throwVMError(exec, createTypeError(exec, "Object.isExtensible can only be called on Objects.")); + return JSValue::encode(jsBoolean(asObject(obj)->isExtensible())); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.h b/Source/JavaScriptCore/runtime/ObjectConstructor.h new file mode 100644 index 000000000..87c6414c4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.h @@ -0,0 +1,63 @@ +/* + * 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: + typedef InternalFunction Base; + + static ObjectConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ObjectPrototype* objectPrototype) + { + ObjectConstructor* constructor = new (NotNull, allocateCell<ObjectConstructor>(*exec->heap())) ObjectConstructor(globalObject, structure); + constructor->finishCreation(exec, objectPrototype); + return constructor; + } + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(ExecState*, ObjectPrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; + + private: + ObjectConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, 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 000000000..3f4dc19db --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 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" + +namespace JSC { + +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*); + +} + +#include "ObjectPrototype.lut.h" + +namespace JSC { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ObjectPrototype); + +const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, 0, ExecState::objectPrototypeTable, CREATE_METHOD_TABLE(ObjectPrototype) }; + +/* Source for ObjectPrototype.lut.h +@begin objectPrototypeTable + toString objectProtoFuncToString DontEnum|Function 0 + toLocaleString objectProtoFuncToLocaleString DontEnum|Function 0 + valueOf objectProtoFuncValueOf DontEnum|Function 0 + hasOwnProperty objectProtoFuncHasOwnProperty DontEnum|Function 1 + propertyIsEnumerable objectProtoFuncPropertyIsEnumerable DontEnum|Function 1 + isPrototypeOf objectProtoFuncIsPrototypeOf DontEnum|Function 1 + __defineGetter__ objectProtoFuncDefineGetter DontEnum|Function 2 + __defineSetter__ objectProtoFuncDefineSetter DontEnum|Function 2 + __lookupGetter__ objectProtoFuncLookupGetter DontEnum|Function 1 + __lookupSetter__ objectProtoFuncLookupSetter DontEnum|Function 1 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype); + +ObjectPrototype::ObjectPrototype(ExecState* exec, Structure* stucture) + : JSNonFinalObject(exec->globalData(), stucture) + , m_hasNoPropertiesWithUInt32Names(true) +{ +} + +void ObjectPrototype::finishCreation(JSGlobalData& globalData, JSGlobalObject*) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); +} + +void ObjectPrototype::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell); + JSNonFinalObject::put(cell, exec, propertyName, value, slot); + + if (thisObject->m_hasNoPropertiesWithUInt32Names) { + bool isUInt32; + propertyName.toUInt32(isUInt32); + thisObject->m_hasNoPropertiesWithUInt32Names = !isUInt32; + } +} + +bool ObjectPrototype::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell); + if (thisObject->m_hasNoPropertiesWithUInt32Names) + return false; + return JSNonFinalObject::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot); +} + +bool ObjectPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<JSNonFinalObject>(exec, ExecState::objectPrototypeTable(exec), jsCast<ObjectPrototype*>(cell), propertyName, slot); +} + +bool ObjectPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<JSNonFinalObject>(exec, ExecState::objectPrototypeTable(exec), jsCast<ObjectPrototype*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions -------------------------------- + +EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toObject(exec)); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsBoolean(thisValue.toObject(exec)->hasOwnProperty(exec, Identifier(exec, exec->argument(0).toString(exec))))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSObject* thisObj = thisValue.toObject(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) +{ + JSObject* thisObject = exec->hostThisValue().toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData callData; + if (getCallData(exec->argument(1), callData) == CallTypeNone) + return throwVMError(exec, createSyntaxError(exec, "invalid getter usage")); + thisObject->methodTable()->defineGetter(thisObject, exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1)), 0); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec) +{ + JSObject* thisObject = exec->hostThisValue().toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData callData; + if (getCallData(exec->argument(1), callData) == CallTypeNone) + return throwVMError(exec, createSyntaxError(exec, "invalid setter usage")); + thisObject->methodTable()->defineSetter(thisObject, exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1)), 0); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec) +{ + JSObject* thisObject = exec->hostThisValue().toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(thisObject->lookupGetter(exec, Identifier(exec, exec->argument(0).toString(exec)))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec) +{ + JSObject* thisObject = exec->hostThisValue().toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(thisObject->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.toObject(exec)->propertyIsEnumerable(exec, Identifier(exec, exec->argument(0).toString(exec))))); +} + +// 15.2.4.3 Object.prototype.toLocaleString() +EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec) +{ + // 1. Let O be the result of calling ToObject passing the this value as the argument. + JSObject* object = exec->hostThisValue().toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 2. Let toString be the result of calling the [[Get]] internal method of O passing "toString" as the argument. + JSValue toString = object->get(exec, exec->propertyNames().toString); + + // 3. If IsCallable(toString) is false, throw a TypeError exception. + CallData callData; + CallType callType = getCallData(toString, callData); + if (callType == CallTypeNone) + return JSValue::encode(jsUndefined()); + + // 4. Return the result of calling the [[Call]] internal method of toString passing O as the this value and no arguments. + return JSValue::encode(call(exec, toString, callType, callData, object, exec->emptyList())); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) + return JSValue::encode(jsNontrivialString(exec, thisValue.isUndefined() ? "[object Undefined]" : "[object Null]")); + JSObject* thisObject = thisValue.toObject(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]")); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.h b/Source/JavaScriptCore/runtime/ObjectPrototype.h new file mode 100644 index 000000000..78b1cbd39 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 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 JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + static ObjectPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + ObjectPrototype* prototype = new (NotNull, allocateCell<ObjectPrototype>(*exec->heap())) ObjectPrototype(exec, structure); + prototype->finishCreation(exec->globalData(), globalObject); + return prototype; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSNonFinalObject::StructureFlags; + + void finishCreation(JSGlobalData&, JSGlobalObject*); + + private: + ObjectPrototype(ExecState*, Structure*); + static void put(JSCell*, ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + 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 000000000..b89746f3f --- /dev/null +++ b/Source/JavaScriptCore/runtime/Operations.cpp @@ -0,0 +1,114 @@ +/* + * 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), jsString(callFrame, p2.toString(callFrame))); + } + if (p2.isString()) + return jsString(callFrame, 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; + JSObject* object = asObject(v); + if (object->methodTable()->getCallData(object, 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; + JSObject* object = asObject(v); + if (object->methodTable()->getCallData(object, callData) != CallTypeNone) + return false; + } + return true; +} + +bool jsIsFunctionType(JSValue v) +{ + if (v.isObject()) { + CallData callData; + JSObject* object = asObject(v); + if (object->methodTable()->getCallData(object, 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 000000000..ca2174fa1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -0,0 +1,392 @@ +/* + * 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 "JSString.h" +#include "JSValueInlineMethods.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) + { + JSGlobalData& globalData = exec->globalData(); + + unsigned length1 = s1->length(); + if (!length1) + return s2; + unsigned length2 = s2->length(); + if (!length2) + return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + return JSString::create(globalData, s1, s2); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3) + { + JSGlobalData* globalData = &exec->globalData(); + + unsigned length1 = u1.length(); + unsigned length2 = u2.length(); + unsigned length3 = u3.length(); + if (!length1) + return jsString(exec, jsString(globalData, u2), jsString(globalData, u3)); + if (!length2) + return jsString(exec, jsString(globalData, u1), jsString(globalData, u3)); + if (!length3) + return jsString(exec, jsString(globalData, u1), jsString(globalData, u2)); + + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + if ((length1 + length2 + length3) < length3) + return throwOutOfMemoryError(exec); + + return JSString::create(exec->globalData(), jsString(globalData, u1), jsString(globalData, u2), jsString(globalData, u3)); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) + { + JSGlobalData* globalData = &exec->globalData(); + JSString::RopeBuilder ropeBuilder(*globalData); + + unsigned oldLength = 0; + + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (v.isString()) + ropeBuilder.append(asString(v)); + else + ropeBuilder.append(jsString(globalData, v.toString(exec))); + + if (ropeBuilder.length() < oldLength) // True for overflow + return throwOutOfMemoryError(exec); + } + + return ropeBuilder.release(); + } + + ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) + { + JSGlobalData* globalData = &exec->globalData(); + JSString::RopeBuilder ropeBuilder(*globalData); + + if (thisValue.isString()) + ropeBuilder.append(asString(thisValue)); + else + ropeBuilder.append(jsString(globalData, thisValue.toString(exec))); + + unsigned oldLength = 0; + + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); + if (v.isString()) + ropeBuilder.append(asString(v)); + else + ropeBuilder.append(jsString(globalData, v.toString(exec))); + + if (ropeBuilder.length() < oldLength) // True for overflow + return throwOutOfMemoryError(exec); + } + + return 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.asNumber() == v2.asNumber(); + + 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.asBoolean()) == v2.asNumber(); + } else if (v2.isBoolean()) { + if (v1.isNumber()) + return v1.asNumber() == static_cast<double>(v2.asBoolean()); + } + + 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.asNumber() == v2.asNumber(); + + if (!v1.isCell() || !v2.isCell()) + return v1 == v2; + + return strictEqualSlowCaseInline(exec, v1, v2); + } + + // See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct + // evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value + // true, for leftFirst, for '>' pass the value false (and reverse operand order). + template<bool leftFirst> + ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) + { + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() < v2.asInt32(); + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() < v2.asNumber(); + + if (isJSString(v1) && isJSString(v2)) + return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); + + double n1; + double n2; + JSValue p1; + JSValue p2; + bool wasNotString1; + bool wasNotString2; + if (leftFirst) { + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + } else { + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + } + + if (wasNotString1 | wasNotString2) + return n1 < n2; + return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); + } + + // See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct + // evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the + // value true, for leftFirst, for '=>' pass the value false (and reverse operand order). + template<bool leftFirst> + ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) + { + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() <= v2.asInt32(); + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() <= v2.asNumber(); + + if (isJSString(v1) && isJSString(v2)) + return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); + + double n1; + double n2; + JSValue p1; + JSValue p2; + bool wasNotString1; + bool wasNotString2; + if (leftFirst) { + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + } else { + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + } + + 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) + { + if (v1.isNumber() && v2.isNumber()) + return jsNumber(v1.asNumber() + v2.asNumber()); + + 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(callFrame->globalData()); + if (slotBase == cell) + slotOffset = cell->structure()->get(callFrame->globalData(), 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(callFrame->globalData()); + + ++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->get(); + if (next == end) { + if (isStrictPut && !base->getPropertySlot(callFrame, property, slot)) + return JSValue(); + return 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/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp new file mode 100644 index 000000000..68d10b7bd --- /dev/null +++ b/Source/JavaScriptCore/runtime/Options.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2011 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 "Options.h" + +#include <limits> +#include <wtf/PageBlock.h> + +#if OS(DARWIN) && ENABLE(PARALLEL_GC) +#include <sys/sysctl.h> +#endif + +// Set to 1 to control the heuristics using environment variables. +#define ENABLE_RUN_TIME_HEURISTICS 0 + +#if ENABLE(RUN_TIME_HEURISTICS) +#include <stdio.h> +#include <stdlib.h> +#include <wtf/StdLibExtras.h> +#endif + +namespace JSC { namespace Options { + +unsigned maximumOptimizationCandidateInstructionCount; + +unsigned maximumFunctionForCallInlineCandidateInstructionCount; +unsigned maximumFunctionForConstructInlineCandidateInstructionCount; + +unsigned maximumInliningDepth; + +int32_t executionCounterValueForOptimizeAfterWarmUp; +int32_t executionCounterValueForOptimizeAfterLongWarmUp; +int32_t executionCounterValueForDontOptimizeAnytimeSoon; +int32_t executionCounterValueForOptimizeSoon; +int32_t executionCounterValueForOptimizeNextInvocation; + +int32_t executionCounterIncrementForLoop; +int32_t executionCounterIncrementForReturn; + +unsigned desiredSpeculativeSuccessFailRatio; + +double likelyToTakeSlowCaseThreshold; +double couldTakeSlowCaseThreshold; +unsigned likelyToTakeSlowCaseMinimumCount; +unsigned couldTakeSlowCaseMinimumCount; + +double osrExitProminenceForFrequentExitSite; + +unsigned largeFailCountThresholdBase; +unsigned largeFailCountThresholdBaseForLoop; + +unsigned reoptimizationRetryCounterMax; +unsigned reoptimizationRetryCounterStep; + +unsigned minimumOptimizationDelay; +unsigned maximumOptimizationDelay; +double desiredProfileLivenessRate; +double desiredProfileFullnessRate; + +double doubleVoteRatioForDoubleFormat; + +unsigned minimumNumberOfScansBetweenRebalance; +unsigned gcMarkStackSegmentSize; +unsigned minimumNumberOfCellsToKeep; +unsigned maximumNumberOfSharedSegments; +unsigned sharedStackWakeupThreshold; +unsigned numberOfGCMarkers; +unsigned opaqueRootMergeThreshold; + +#if ENABLE(RUN_TIME_HEURISTICS) +static bool parse(const char* string, int32_t& value) +{ + return sscanf(string, "%d", &value) == 1; +} + +static bool parse(const char* string, unsigned& value) +{ + return sscanf(string, "%u", &value) == 1; +} + +static bool parse(const char* string, double& value) +{ + return sscanf(string, "%lf", &value) == 1; +} + +template<typename T, typename U> +void setHeuristic(T& variable, const char* name, U value) +{ + const char* stringValue = getenv(name); + if (!stringValue) { + variable = safeCast<T>(value); + return; + } + + if (parse(stringValue, variable)) + return; + + fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue); + variable = safeCast<T>(value); +} + +#define SET(variable, value) setHeuristic(variable, "JSC_" #variable, value) +#else +#define SET(variable, value) variable = value +#endif + +void initializeOptions() +{ + SET(maximumOptimizationCandidateInstructionCount, 1000); + + SET(maximumFunctionForCallInlineCandidateInstructionCount, 150); + SET(maximumFunctionForConstructInlineCandidateInstructionCount, 80); + + SET(maximumInliningDepth, 5); + + SET(executionCounterValueForOptimizeAfterWarmUp, -1000); + SET(executionCounterValueForOptimizeAfterLongWarmUp, -5000); + SET(executionCounterValueForDontOptimizeAnytimeSoon, std::numeric_limits<int32_t>::min()); + SET(executionCounterValueForOptimizeSoon, -1000); + SET(executionCounterValueForOptimizeNextInvocation, 0); + + SET(executionCounterIncrementForLoop, 1); + SET(executionCounterIncrementForReturn, 15); + + SET(desiredSpeculativeSuccessFailRatio, 6); + + SET(likelyToTakeSlowCaseThreshold, 0.15); + SET(couldTakeSlowCaseThreshold, 0.05); // Shouldn't be zero because some ops will spuriously take slow case, for example for linking or caching. + SET(likelyToTakeSlowCaseMinimumCount, 100); + SET(couldTakeSlowCaseMinimumCount, 10); + + SET(osrExitProminenceForFrequentExitSite, 0.3); + + SET(largeFailCountThresholdBase, 20); + SET(largeFailCountThresholdBaseForLoop, 1); + + SET(reoptimizationRetryCounterStep, 1); + + SET(minimumOptimizationDelay, 1); + SET(maximumOptimizationDelay, 5); + SET(desiredProfileLivenessRate, 0.75); + SET(desiredProfileFullnessRate, 0.35); + + SET(doubleVoteRatioForDoubleFormat, 2); + + SET(minimumNumberOfScansBetweenRebalance, 10000); + SET(gcMarkStackSegmentSize, pageSize()); + SET(minimumNumberOfCellsToKeep, 10); + SET(maximumNumberOfSharedSegments, 3); + SET(sharedStackWakeupThreshold, 1); + SET(opaqueRootMergeThreshold, 1000); + + int cpusToUse = 1; +#if OS(DARWIN) && ENABLE(PARALLEL_GC) + int name[2]; + size_t valueSize = sizeof(cpusToUse); + name[0] = CTL_HW; + name[1] = HW_AVAILCPU; + sysctl(name, 2, &cpusToUse, &valueSize, 0, 0); +#endif + // We don't scale so well beyond 4. + if (cpusToUse > 4) + cpusToUse = 4; + // Be paranoid, it is the OS we're dealing with, after all. + if (cpusToUse < 1) + cpusToUse = 1; + + SET(numberOfGCMarkers, cpusToUse); + + ASSERT(executionCounterValueForDontOptimizeAnytimeSoon <= executionCounterValueForOptimizeAfterLongWarmUp); + ASSERT(executionCounterValueForOptimizeAfterLongWarmUp <= executionCounterValueForOptimizeAfterWarmUp); + ASSERT(executionCounterValueForOptimizeAfterWarmUp <= executionCounterValueForOptimizeSoon); + ASSERT(executionCounterValueForOptimizeAfterWarmUp < 0); + ASSERT(executionCounterValueForOptimizeSoon <= executionCounterValueForOptimizeNextInvocation); + + // Compute the maximum value of the reoptimization retry counter. This is simply + // the largest value at which we don't overflow the execute counter, when using it + // to left-shift the execution counter by this amount. Currently the value ends + // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles + // total on a 32-bit processor. + reoptimizationRetryCounterMax = 0; + while ((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) >= static_cast<int64_t>(std::numeric_limits<int32_t>::min())) + reoptimizationRetryCounterMax++; + + ASSERT((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) < 0); + ASSERT((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) >= static_cast<int64_t>(std::numeric_limits<int32_t>::min())); +} + +} } // namespace JSC::Options + + diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h new file mode 100644 index 000000000..f7cc5f4e8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Options.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 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 Options_h +#define Options_h + +#include <stdint.h> + +namespace JSC { namespace Options { + +extern unsigned maximumOptimizationCandidateInstructionCount; + +extern unsigned maximumFunctionForCallInlineCandidateInstructionCount; +extern unsigned maximumFunctionForConstructInlineCandidateInstructionCount; + +extern unsigned maximumInliningDepth; // Depth of inline stack, so 1 = no inlining, 2 = one level, etc. + +extern int32_t executionCounterValueForOptimizeAfterWarmUp; +extern int32_t executionCounterValueForOptimizeAfterLongWarmUp; +extern int32_t executionCounterValueForDontOptimizeAnytimeSoon; +extern int32_t executionCounterValueForOptimizeSoon; +extern int32_t executionCounterValueForOptimizeNextInvocation; + +extern int32_t executionCounterIncrementForLoop; +extern int32_t executionCounterIncrementForReturn; + +extern unsigned desiredSpeculativeSuccessFailRatio; + +extern double likelyToTakeSlowCaseThreshold; +extern double couldTakeSlowCaseThreshold; +extern unsigned likelyToTakeSlowCaseMinimumCount; +extern unsigned couldTakeSlowCaseMinimumCount; + +extern double osrExitProminenceForFrequentExitSite; + +extern unsigned largeFailCountThresholdBase; +extern unsigned largeFailCountThresholdBaseForLoop; + +extern unsigned reoptimizationRetryCounterMax; +extern unsigned reoptimizationRetryCounterStep; + +extern unsigned minimumOptimizationDelay; +extern unsigned maximumOptimizationDelay; +extern double desiredProfileLivenessRate; +extern double desiredProfileFullnessRate; + +extern double doubleVoteRatioForDoubleFormat; + +extern unsigned minimumNumberOfScansBetweenRebalance; +extern unsigned gcMarkStackSegmentSize; +extern unsigned minimumNumberOfCellsToKeep; +extern unsigned maximumNumberOfSharedSegments; +extern unsigned sharedStackWakeupThreshold; +extern unsigned numberOfGCMarkers; +extern unsigned opaqueRootMergeThreshold; + +void initializeOptions(); + +} } // namespace JSC::Options + +#endif // Options_h + diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp new file mode 100644 index 000000000..9d62dfc98 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp @@ -0,0 +1,204 @@ +/* + * 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 (value.isGetterSetter()) { + GetterSetter* accessor = asGetterSetter(value); + + m_getter = accessor->getter(); + if (m_getter) + m_attributes |= Getter; + + m_setter = accessor->setter(); + if (m_setter) + m_attributes |= 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(GetterSetter* accessor, unsigned attributes) +{ + ASSERT(attributes & (Getter | Setter)); + ASSERT(accessor->getter() || accessor->setter()); + ASSERT(!accessor->getter() == !(attributes & Getter)); + ASSERT(!accessor->setter() == !(attributes & Setter)); + m_attributes = attributes; + m_getter = accessor->getter(); + m_setter = accessor->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 000000000..bebf5e826 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.h @@ -0,0 +1,82 @@ +/* + * 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 GetterSetter; + + 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(GetterSetter* accessor, 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 000000000..7c8cabb7b --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h @@ -0,0 +1,591 @@ +/* + * 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 "WriteBarrier.h" +#include <wtf/HashTable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + + +#ifndef NDEBUG +#define DUMP_PROPERTYMAP_STATS 0 +#else +#define DUMP_PROPERTYMAP_STATS 0 +#endif + +#if DUMP_PROPERTYMAP_STATS + +extern int numProbes; +extern int numCollisions; +extern int numRehashes; +extern int numRemoves; + +#endif + +#define PROPERTY_MAP_DELETED_ENTRY_KEY ((StringImpl*)1) + +namespace JSC { + +inline bool isPowerOf2(unsigned v) +{ + // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html + + return !(v & (v - 1)) && v; +} + +inline 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; +} + +struct PropertyMapEntry { + StringImpl* key; + unsigned offset; + unsigned attributes; + WriteBarrier<JSCell> specificValue; + + PropertyMapEntry(JSGlobalData& globalData, JSCell* owner, StringImpl* key, unsigned offset, unsigned attributes, JSCell* specificValue) + : key(key) + , offset(offset) + , attributes(attributes) + , specificValue(globalData, owner, specificValue, WriteBarrier<JSCell>::MayBeNull) + { + } +}; + +class PropertyTable { + WTF_MAKE_FAST_ALLOCATED; + + // This is the implementation for 'iterator' and 'const_iterator', + // used for iterating over the table in insertion order. + template<typename T> + class ordered_iterator { + public: + ordered_iterator<T>& operator++() + { + m_valuePtr = skipDeletedEntries(m_valuePtr + 1); + return *this; + } + + bool operator==(const ordered_iterator<T>& other) + { + return m_valuePtr == other.m_valuePtr; + } + + bool operator!=(const ordered_iterator<T>& other) + { + return m_valuePtr != other.m_valuePtr; + } + + T& operator*() + { + return *m_valuePtr; + } + + T* operator->() + { + return m_valuePtr; + } + + ordered_iterator(T* valuePtr) + : m_valuePtr(valuePtr) + { + } + + private: + T* m_valuePtr; + }; + +public: + typedef StringImpl* KeyType; + typedef PropertyMapEntry ValueType; + + // The in order iterator provides overloaded * and -> to access the Value at the current position. + typedef ordered_iterator<ValueType> iterator; + typedef ordered_iterator<const ValueType> const_iterator; + + // The find_iterator is a pair of a pointer to a Value* an the entry in the index. + // If 'find' does not find an entry then iter.first will be 0, and iter.second will + // give the point in m_index where an entry should be inserted. + typedef std::pair<ValueType*, unsigned> find_iterator; + + // Constructor is passed an initial capacity, a PropertyTable to copy, or both. + explicit PropertyTable(unsigned initialCapacity); + PropertyTable(JSGlobalData&, JSCell*, const PropertyTable&); + PropertyTable(JSGlobalData&, JSCell*, unsigned initialCapacity, const PropertyTable&); + ~PropertyTable(); + + // Ordered iteration methods. + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + // Find a value in the table. + find_iterator find(const KeyType&); + find_iterator findWithString(const KeyType&); + // Add a value to the table + std::pair<find_iterator, bool> add(const ValueType& entry); + // Remove a value from the table. + void remove(const find_iterator& iter); + void remove(const KeyType& key); + + // Returns the number of values in the hashtable. + unsigned size() const; + + // Checks if there are any values in the hashtable. + bool isEmpty() const; + + // Number of slots in the property storage array in use, included deletedOffsets. + unsigned propertyStorageSize() const; + + // Used to maintain a list of unused entries in the property storage. + void clearDeletedOffsets(); + bool hasDeletedOffset(); + unsigned getDeletedOffset(); + void addDeletedOffset(unsigned offset); + + // Copy this PropertyTable, ensuring the copy has at least the capacity provided. + PassOwnPtr<PropertyTable> copy(JSGlobalData&, JSCell* owner, unsigned newCapacity); + +#ifndef NDEBUG + size_t sizeInMemory(); + void checkConsistency(); +#endif + +private: + PropertyTable(const PropertyTable&); + // Used to insert a value known not to be in the table, and where we know capacity to be available. + void reinsert(const ValueType& entry); + + // Rehash the table. Used to grow, or to recover deleted slots. + void rehash(unsigned newCapacity); + + // The capacity of the table of values is half of the size of the index. + unsigned tableCapacity() const; + + // We keep an extra deleted slot after the array to make iteration work, + // and to use for deleted values. Index values into the array are 1-based, + // so this is tableCapacity() + 1. + // For example, if m_tableSize is 16, then tableCapacity() is 8 - but the + // values array is actually 9 long (the 9th used for the deleted value/ + // iteration guard). The 8 valid entries are numbered 1..8, so the + // deleted index is 9 (0 being reserved for empty). + unsigned deletedEntryIndex() const; + + // Used in iterator creation/progression. + template<typename T> + static T* skipDeletedEntries(T* valuePtr); + + // The table of values lies after the hash index. + ValueType* table(); + const ValueType* table() const; + + // total number of used entries in the values array - by either valid entries, or deleted ones. + unsigned usedCount() const; + + // The size in bytes of data needed for by the table. + size_t dataSize(); + + // Calculates the appropriate table size (rounds up to a power of two). + static unsigned sizeForCapacity(unsigned capacity); + + // Check if capacity is available. + bool canInsert(); + + unsigned m_indexSize; + unsigned m_indexMask; + unsigned* m_index; + unsigned m_keyCount; + unsigned m_deletedCount; + OwnPtr< Vector<unsigned> > m_deletedOffsets; + + static const unsigned MinimumTableSize = 16; + static const unsigned EmptyEntryIndex = 0; +}; + +inline PropertyTable::PropertyTable(unsigned initialCapacity) + : m_indexSize(sizeForCapacity(initialCapacity)) + , m_indexMask(m_indexSize - 1) + , m_index(static_cast<unsigned*>(fastZeroedMalloc(dataSize()))) + , m_keyCount(0) + , m_deletedCount(0) +{ + ASSERT(isPowerOf2(m_indexSize)); +} + +inline PropertyTable::PropertyTable(JSGlobalData&, JSCell* owner, const PropertyTable& other) + : m_indexSize(other.m_indexSize) + , m_indexMask(other.m_indexMask) + , m_index(static_cast<unsigned*>(fastMalloc(dataSize()))) + , m_keyCount(other.m_keyCount) + , m_deletedCount(other.m_deletedCount) +{ + ASSERT(isPowerOf2(m_indexSize)); + + memcpy(m_index, other.m_index, dataSize()); + + iterator end = this->end(); + for (iterator iter = begin(); iter != end; ++iter) { + iter->key->ref(); + Heap::writeBarrier(owner, iter->specificValue.get()); + } + + // Copy the m_deletedOffsets vector. + Vector<unsigned>* otherDeletedOffsets = other.m_deletedOffsets.get(); + if (otherDeletedOffsets) + m_deletedOffsets = adoptPtr(new Vector<unsigned>(*otherDeletedOffsets)); +} + +inline PropertyTable::PropertyTable(JSGlobalData&, JSCell* owner, unsigned initialCapacity, const PropertyTable& other) + : m_indexSize(sizeForCapacity(initialCapacity)) + , m_indexMask(m_indexSize - 1) + , m_index(static_cast<unsigned*>(fastZeroedMalloc(dataSize()))) + , m_keyCount(0) + , m_deletedCount(0) +{ + ASSERT(isPowerOf2(m_indexSize)); + ASSERT(initialCapacity >= other.m_keyCount); + + const_iterator end = other.end(); + for (const_iterator iter = other.begin(); iter != end; ++iter) { + ASSERT(canInsert()); + reinsert(*iter); + iter->key->ref(); + Heap::writeBarrier(owner, iter->specificValue.get()); + } + + // Copy the m_deletedOffsets vector. + Vector<unsigned>* otherDeletedOffsets = other.m_deletedOffsets.get(); + if (otherDeletedOffsets) + m_deletedOffsets = adoptPtr(new Vector<unsigned>(*otherDeletedOffsets)); +} + +inline PropertyTable::~PropertyTable() +{ + iterator end = this->end(); + for (iterator iter = begin(); iter != end; ++iter) + iter->key->deref(); + + fastFree(m_index); +} + +inline PropertyTable::iterator PropertyTable::begin() +{ + return iterator(skipDeletedEntries(table())); +} + +inline PropertyTable::iterator PropertyTable::end() +{ + return iterator(table() + usedCount()); +} + +inline PropertyTable::const_iterator PropertyTable::begin() const +{ + return const_iterator(skipDeletedEntries(table())); +} + +inline PropertyTable::const_iterator PropertyTable::end() const +{ + return const_iterator(table() + usedCount()); +} + +inline PropertyTable::find_iterator PropertyTable::find(const KeyType& key) +{ + ASSERT(key); + ASSERT(key->isIdentifier()); + unsigned hash = key->existingHash(); + unsigned step = 0; + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + while (true) { + unsigned entryIndex = m_index[hash & m_indexMask]; + if (entryIndex == EmptyEntryIndex) + return std::make_pair((ValueType*)0, hash & m_indexMask); + if (key == table()[entryIndex - 1].key) + return std::make_pair(&table()[entryIndex - 1], hash & m_indexMask); + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + if (!step) + step = WTF::doubleHash(key->existingHash()) | 1; + hash += step; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } +} + +inline PropertyTable::find_iterator PropertyTable::findWithString(const KeyType& key) +{ + ASSERT(key); + ASSERT(!key->isIdentifier() && !key->hasHash()); + unsigned hash = key->hash(); + unsigned step = 0; + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + while (true) { + unsigned entryIndex = m_index[hash & m_indexMask]; + if (entryIndex == EmptyEntryIndex) + return std::make_pair((ValueType*)0, hash & m_indexMask); + if (equal(key, table()[entryIndex - 1].key)) + return std::make_pair(&table()[entryIndex - 1], hash & m_indexMask); + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + if (!step) + step = WTF::doubleHash(key->existingHash()) | 1; + hash += step; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } +} + +inline std::pair<PropertyTable::find_iterator, bool> PropertyTable::add(const ValueType& entry) +{ + // Look for a value with a matching key already in the array. + find_iterator iter = find(entry.key); + if (iter.first) + return std::make_pair(iter, false); + + // Ref the key + entry.key->ref(); + + // ensure capacity is available. + if (!canInsert()) { + rehash(m_keyCount + 1); + iter = find(entry.key); + ASSERT(!iter.first); + } + + // Allocate a slot in the hashtable, and set the index to reference this. + unsigned entryIndex = usedCount() + 1; + m_index[iter.second] = entryIndex; + iter.first = &table()[entryIndex - 1]; + *iter.first = entry; + + ++m_keyCount; + return std::make_pair(iter, true); +} + +inline void PropertyTable::remove(const find_iterator& iter) +{ + // Removing a key that doesn't exist does nothing! + if (!iter.first) + return; + +#if DUMP_PROPERTYMAP_STATS + ++numRemoves; +#endif + + // Replace this one element with the deleted sentinel. Also clear out + // the entry so we can iterate all the entries as needed. + m_index[iter.second] = deletedEntryIndex(); + iter.first->key->deref(); + iter.first->key = PROPERTY_MAP_DELETED_ENTRY_KEY; + + ASSERT(m_keyCount >= 1); + --m_keyCount; + ++m_deletedCount; + + if (m_deletedCount * 4 >= m_indexSize) + rehash(m_keyCount); +} + +inline void PropertyTable::remove(const KeyType& key) +{ + remove(find(key)); +} + +// returns the number of values in the hashtable. +inline unsigned PropertyTable::size() const +{ + return m_keyCount; +} + +inline bool PropertyTable::isEmpty() const +{ + return !m_keyCount; +} + +inline unsigned PropertyTable::propertyStorageSize() const +{ + return size() + (m_deletedOffsets ? m_deletedOffsets->size() : 0); +} + +inline void PropertyTable::clearDeletedOffsets() +{ + m_deletedOffsets.clear(); +} + +inline bool PropertyTable::hasDeletedOffset() +{ + return m_deletedOffsets && !m_deletedOffsets->isEmpty(); +} + +inline unsigned PropertyTable::getDeletedOffset() +{ + unsigned offset = m_deletedOffsets->last(); + m_deletedOffsets->removeLast(); + return offset; +} + +inline void PropertyTable::addDeletedOffset(unsigned offset) +{ + if (!m_deletedOffsets) + m_deletedOffsets = adoptPtr(new Vector<unsigned>); + m_deletedOffsets->append(offset); +} + +inline PassOwnPtr<PropertyTable> PropertyTable::copy(JSGlobalData& globalData, JSCell* owner, unsigned newCapacity) +{ + ASSERT(newCapacity >= m_keyCount); + + // Fast case; if the new table will be the same m_indexSize as this one, we can memcpy it, + // save rehashing all keys. + if (sizeForCapacity(newCapacity) == m_indexSize) + return adoptPtr(new PropertyTable(globalData, owner, *this)); + return adoptPtr(new PropertyTable(globalData, owner, newCapacity, *this)); +} + +#ifndef NDEBUG +inline size_t PropertyTable::sizeInMemory() +{ + size_t result = sizeof(PropertyTable) + dataSize(); + if (m_deletedOffsets) + result += (m_deletedOffsets->capacity() * sizeof(unsigned)); + return result; +} +#endif + +inline void PropertyTable::reinsert(const ValueType& entry) +{ + // Used to insert a value known not to be in the table, and where + // we know capacity to be available. + ASSERT(canInsert()); + find_iterator iter = find(entry.key); + ASSERT(!iter.first); + + unsigned entryIndex = usedCount() + 1; + m_index[iter.second] = entryIndex; + table()[entryIndex - 1] = entry; + + ++m_keyCount; +} + +inline void PropertyTable::rehash(unsigned newCapacity) +{ + unsigned* oldEntryIndices = m_index; + iterator iter = this->begin(); + iterator end = this->end(); + + m_indexSize = sizeForCapacity(newCapacity); + m_indexMask = m_indexSize - 1; + m_keyCount = 0; + m_deletedCount = 0; + m_index = static_cast<unsigned*>(fastZeroedMalloc(dataSize())); + + for (; iter != end; ++iter) { + ASSERT(canInsert()); + reinsert(*iter); + } + + fastFree(oldEntryIndices); +} + +inline unsigned PropertyTable::tableCapacity() const { return m_indexSize >> 1; } + +inline unsigned PropertyTable::deletedEntryIndex() const { return tableCapacity() + 1; } + +template<typename T> +inline T* PropertyTable::skipDeletedEntries(T* valuePtr) +{ + while (valuePtr->key == PROPERTY_MAP_DELETED_ENTRY_KEY) + ++valuePtr; + return valuePtr; +} + +inline PropertyTable::ValueType* PropertyTable::table() +{ + // The table of values lies after the hash index. + return reinterpret_cast<ValueType*>(m_index + m_indexSize); +} + +inline const PropertyTable::ValueType* PropertyTable::table() const +{ + // The table of values lies after the hash index. + return reinterpret_cast<const ValueType*>(m_index + m_indexSize); +} + +inline unsigned PropertyTable::usedCount() const +{ + // Total number of used entries in the values array - by either valid entries, or deleted ones. + return m_keyCount + m_deletedCount; +} + +inline size_t PropertyTable::dataSize() +{ + // The size in bytes of data needed for by the table. + return m_indexSize * sizeof(unsigned) + ((tableCapacity()) + 1) * sizeof(ValueType); +} + +inline unsigned PropertyTable::sizeForCapacity(unsigned capacity) +{ + if (capacity < 8) + return MinimumTableSize; + return nextPowerOf2(capacity + 1) * 2; +} + +inline bool PropertyTable::canInsert() +{ + return usedCount() < tableCapacity(); +} + +} // namespace JSC + +#endif // PropertyMapHashTable_h diff --git a/Source/JavaScriptCore/runtime/PropertyNameArray.cpp b/Source/JavaScriptCore/runtime/PropertyNameArray.cpp new file mode 100644 index 000000000..8efb4065e --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyNameArray.cpp @@ -0,0 +1,55 @@ +/* + * 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 "JSObject.h" +#include "ScopeChain.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 000000000..0da930f17 --- /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 000000000..edabd7a6e --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertySlot.cpp @@ -0,0 +1,44 @@ +/* + * 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->methodTable()->getCallData(m_data.getterFunc, callData); + + // Only objects can have accessor properties. + // If the base is WebCore's global object then we need to substitute the shell. + ASSERT(m_slotBase.isObject()); + return call(exec, m_data.getterFunc, callType, callData, m_thisValue.toThisObject(exec), exec->emptyList()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PropertySlot.h b/Source/JavaScriptCore/runtime/PropertySlot.h new file mode 100644 index 000000000..8557e6d24 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertySlot.h @@ -0,0 +1,241 @@ +/* + * 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_MARKER 0 +#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_MARKER) + return m_value; + 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_MARKER) + return m_value; + 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 setValue(JSValue slotBase, JSValue value) + { + ASSERT(value); + clearOffset(); + m_getValue = JSC_VALUE_MARKER; + m_slotBase = slotBase; + m_value = value; + } + + void setValue(JSValue slotBase, JSValue value, size_t offset) + { + ASSERT(value); + m_getValue = JSC_VALUE_MARKER; + m_slotBase = slotBase; + m_value = value; + m_offset = offset; + m_cachedPropertyType = Value; + } + + void setValue(JSValue value) + { + ASSERT(value); + clearBase(); + clearOffset(); + m_getValue = JSC_VALUE_MARKER; + m_value = value; + } + + 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; } + + 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; + 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 000000000..843c9e111 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Protect.h @@ -0,0 +1,66 @@ +/* + * 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 "Heap.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()); + } + +} // namespace JSC + +#endif // Protect_h diff --git a/Source/JavaScriptCore/runtime/PutPropertySlot.h b/Source/JavaScriptCore/runtime/PutPropertySlot.h new file mode 100644 index 000000000..69d1f8bd2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PutPropertySlot.h @@ -0,0 +1,81 @@ +// -*- 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 000000000..0d513d2cc --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -0,0 +1,486 @@ +/* + * 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 "RegExpCache.h" +#include "yarr/Yarr.h" +#include "yarr/YarrJIT.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> + + +#define REGEXP_FUNC_TEST_DATA_GEN 0 + +namespace JSC { + +const ClassInfo RegExp::s_info = { "RegExp", 0, 0, 0, CREATE_METHOD_TABLE(RegExp) }; + +RegExpFlags regExpFlags(const UString& string) +{ + RegExpFlags flags = NoFlags; + + for (unsigned i = 0; i < string.length(); ++i) { + switch (string[i]) { + case 'g': + if (flags & FlagGlobal) + return InvalidFlags; + flags = static_cast<RegExpFlags>(flags | FlagGlobal); + break; + + case 'i': + if (flags & FlagIgnoreCase) + return InvalidFlags; + flags = static_cast<RegExpFlags>(flags | FlagIgnoreCase); + break; + + case 'm': + if (flags & FlagMultiline) + return InvalidFlags; + flags = static_cast<RegExpFlags>(flags | FlagMultiline); + break; + + default: + return InvalidFlags; + } + } + + return flags; +} + +#if REGEXP_FUNC_TEST_DATA_GEN +class RegExpFunctionalTestCollector { + // This class is not thread safe. +protected: + static const char* const s_fileName; + +public: + static RegExpFunctionalTestCollector* get(); + + ~RegExpFunctionalTestCollector(); + + void outputOneTest(RegExp*, UString, int, int*, int); + void clearRegExp(RegExp* regExp) + { + if (regExp == m_lastRegExp) + m_lastRegExp = 0; + } + +private: + RegExpFunctionalTestCollector(); + + void outputEscapedUString(const UString&, bool escapeSlash = false); + + static RegExpFunctionalTestCollector* s_instance; + FILE* m_file; + RegExp* m_lastRegExp; +}; + +const char* const RegExpFunctionalTestCollector::s_fileName = "/tmp/RegExpTestsData"; +RegExpFunctionalTestCollector* RegExpFunctionalTestCollector::s_instance = 0; + +RegExpFunctionalTestCollector* RegExpFunctionalTestCollector::get() +{ + if (!s_instance) + s_instance = new RegExpFunctionalTestCollector(); + + return s_instance; +} + +void RegExpFunctionalTestCollector::outputOneTest(RegExp* regExp, UString s, int startOffset, int* ovector, int result) +{ + if ((!m_lastRegExp) || (m_lastRegExp != regExp)) { + m_lastRegExp = regExp; + fputc('/', m_file); + outputEscapedUString(regExp->pattern(), true); + fputc('/', m_file); + if (regExp->global()) + fputc('g', m_file); + if (regExp->ignoreCase()) + fputc('i', m_file); + if (regExp->multiline()) + fputc('m', m_file); + fprintf(m_file, "\n"); + } + + fprintf(m_file, " \""); + outputEscapedUString(s); + fprintf(m_file, "\", %d, %d, (", startOffset, result); + for (unsigned i = 0; i <= regExp->numSubpatterns(); i++) { + int subPatternBegin = ovector[i * 2]; + int subPatternEnd = ovector[i * 2 + 1]; + if (subPatternBegin == -1) + subPatternEnd = -1; + fprintf(m_file, "%d, %d", subPatternBegin, subPatternEnd); + if (i < regExp->numSubpatterns()) + fputs(", ", m_file); + } + + fprintf(m_file, ")\n"); + fflush(m_file); +} + +RegExpFunctionalTestCollector::RegExpFunctionalTestCollector() +{ + m_file = fopen(s_fileName, "r+"); + if (!m_file) + m_file = fopen(s_fileName, "w+"); + + fseek(m_file, 0L, SEEK_END); +} + +RegExpFunctionalTestCollector::~RegExpFunctionalTestCollector() +{ + fclose(m_file); + s_instance = 0; +} + +void RegExpFunctionalTestCollector::outputEscapedUString(const UString& s, bool escapeSlash) +{ + int len = s.length(); + + for (int i = 0; i < len; ++i) { + UChar c = s[i]; + + switch (c) { + case '\0': + fputs("\\0", m_file); + break; + case '\a': + fputs("\\a", m_file); + break; + case '\b': + fputs("\\b", m_file); + break; + case '\f': + fputs("\\f", m_file); + break; + case '\n': + fputs("\\n", m_file); + break; + case '\r': + fputs("\\r", m_file); + break; + case '\t': + fputs("\\t", m_file); + break; + case '\v': + fputs("\\v", m_file); + break; + case '/': + if (escapeSlash) + fputs("\\/", m_file); + else + fputs("/", m_file); + break; + case '\"': + fputs("\\\"", m_file); + break; + case '\\': + fputs("\\\\", m_file); + break; + case '\?': + fputs("\?", m_file); + break; + default: + if (c > 0x7f) + fprintf(m_file, "\\u%04x", c); + else + fputc(c, m_file); + break; + } + } +} +#endif + +struct RegExpRepresentation { +#if ENABLE(YARR_JIT) + Yarr::YarrCodeBlock m_regExpJITCode; +#endif + OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; +}; + +RegExp::RegExp(JSGlobalData& globalData, const UString& patternString, RegExpFlags flags) + : JSCell(globalData, globalData.regExpStructure.get()) + , m_state(NotCompiled) + , m_patternString(patternString) + , m_flags(flags) + , m_constructionError(0) + , m_numSubpatterns(0) +#if ENABLE(REGEXP_TRACING) + , m_rtMatchCallCount(0) + , m_rtMatchFoundCount(0) +#endif +{ +} + +void RegExp::finishCreation(JSGlobalData& globalData) +{ + Base::finishCreation(globalData); + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) + m_state = ParseError; + else + m_numSubpatterns = pattern.m_numSubpatterns; +} + +void RegExp::destroy(JSCell* cell) +{ + RegExp* thisObject = jsCast<RegExp*>(cell); +#if REGEXP_FUNC_TEST_DATA_GEN + RegExpFunctionalTestCollector::get()->clearRegExp(this); +#endif + thisObject->RegExp::~RegExp(); +} + +RegExp* RegExp::createWithoutCaching(JSGlobalData& globalData, const UString& patternString, RegExpFlags flags) +{ + RegExp* regExp = new (NotNull, allocateCell<RegExp>(globalData.heap)) RegExp(globalData, patternString, flags); + regExp->finishCreation(globalData); + return regExp; +} + +RegExp* RegExp::create(JSGlobalData& globalData, const UString& patternString, RegExpFlags flags) +{ + return globalData.regExpCache()->lookupOrCreate(patternString, flags); +} + +void RegExp::compile(JSGlobalData* globalData, Yarr::YarrCharSize charSize) +{ + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) { + ASSERT_NOT_REACHED(); + m_state = ParseError; + return; + } + ASSERT(m_numSubpatterns == pattern.m_numSubpatterns); + + if (!m_representation) { + ASSERT(m_state == NotCompiled); + m_representation = adoptPtr(new RegExpRepresentation); + globalData->regExpCache()->addToStrongCache(this); + m_state = ByteCode; + } + +#if ENABLE(YARR_JIT) + if (!pattern.m_containsBackreferences && globalData->canUseJIT()) { + Yarr::jitCompile(pattern, charSize, globalData, m_representation->m_regExpJITCode); +#if ENABLE(YARR_JIT_DEBUG) + if (!m_representation->m_regExpJITCode.isFallBack()) + m_state = JITCode; + else + m_state = ByteCode; +#else + if (!m_representation->m_regExpJITCode.isFallBack()) { + m_state = JITCode; + return; + } +#endif + } +#else + UNUSED_PARAM(charSize); +#endif + + m_representation->m_regExpBytecode = Yarr::byteCompile(pattern, &globalData->m_regExpAllocator); +} + +void RegExp::compileIfNecessary(JSGlobalData& globalData, Yarr::YarrCharSize charSize) +{ + // If the state is NotCompiled or ParseError, then there is no representation. + // If there is a representation, and the state must be either JITCode or ByteCode. + ASSERT(!!m_representation == (m_state == JITCode || m_state == ByteCode)); + + if (m_representation) { +#if ENABLE(YARR_JIT) + if (m_state != JITCode) + return; + if ((charSize == Yarr::Char8) && (m_representation->m_regExpJITCode.has8BitCode())) + return; + if ((charSize == Yarr::Char16) && (m_representation->m_regExpJITCode.has16BitCode())) + return; +#else + return; +#endif + } + + compile(&globalData, charSize); +} + + +int RegExp::match(JSGlobalData& globalData, 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) { + compileIfNecessary(globalData, s.is8Bit() ? Yarr::Char8 : Yarr::Char16); + + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + if (ovector) { + ovector->resize(offsetVectorSize); + offsetVector = ovector->data(); + } else { + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + } + + ASSERT(offsetVector); + // 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) { + if (s.is8Bit()) + result = Yarr::execute(m_representation->m_regExpJITCode, s.characters8(), startOffset, s.length(), offsetVector); + else + result = Yarr::execute(m_representation->m_regExpJITCode, s.characters16(), startOffset, s.length(), offsetVector); +#if ENABLE(YARR_JIT_DEBUG) + matchCompareWithInterpreter(s, startOffset, offsetVector, result); +#endif + } else +#endif + result = Yarr::interpret(m_representation->m_regExpBytecode.get(), s, startOffset, s.length(), offsetVector); + ASSERT(result >= -1); + +#if REGEXP_FUNC_TEST_DATA_GEN + RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result); +#endif + +#if ENABLE(REGEXP_TRACING) + if (result != -1) + m_rtMatchFoundCount++; +#endif + + return result; + } + + return -1; +} + +void RegExp::invalidateCode() +{ + if (!m_representation) + return; + m_state = NotCompiled; + m_representation.clear(); +} + +#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::interpret(m_representation->m_regExpBytecode.get(), s, 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::YarrCodeBlock& codeBlock = m_representation->m_regExpJITCode; + + const size_t jitAddrSize = 20; + char jitAddr[jitAddrSize]; + if (m_state == JITCode) + snprintf(jitAddr, jitAddrSize, "fallback"); + else + snprintf(jitAddr, jitAddrSize, "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 000000000..65eb48499 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExp.h @@ -0,0 +1,116 @@ +/* + * 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 "Structure.h" +#include "RegExpKey.h" +#include "yarr/Yarr.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace JSC { + + struct RegExpRepresentation; + class JSGlobalData; + + RegExpFlags regExpFlags(const UString&); + + class RegExp : public JSCell { + public: + typedef JSCell Base; + + static RegExp* create(JSGlobalData&, const UString& pattern, RegExpFlags); + static void destroy(JSCell*); + + bool global() const { return m_flags & FlagGlobal; } + bool ignoreCase() const { return m_flags & FlagIgnoreCase; } + bool multiline() const { return m_flags & FlagMultiline; } + + const UString& pattern() const { return m_patternString; } + + bool isValid() const { return !m_constructionError && m_flags != InvalidFlags; } + const char* errorMessage() const { return m_constructionError; } + + int match(JSGlobalData&, const UString&, int startOffset, Vector<int, 32>* ovector = 0); + unsigned numSubpatterns() const { return m_numSubpatterns; } + + bool hasCode() + { + return m_representation; + } + + void invalidateCode(); + +#if ENABLE(REGEXP_TRACING) + void printTraceData(); +#endif + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(LeafType, 0), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; + + RegExpKey key() { return RegExpKey(m_flags, m_patternString); } + + protected: + void finishCreation(JSGlobalData&); + + private: + friend class RegExpCache; + RegExp(JSGlobalData&, const UString&, RegExpFlags); + + static RegExp* createWithoutCaching(JSGlobalData&, const UString&, RegExpFlags); + + enum RegExpState { + ParseError, + JITCode, + ByteCode, + NotCompiled + } m_state; + + void compile(JSGlobalData*, Yarr::YarrCharSize); + void compileIfNecessary(JSGlobalData&, Yarr::YarrCharSize); + +#if ENABLE(YARR_JIT_DEBUG) + void matchCompareWithInterpreter(const UString&, int startOffset, int* offsetVector, int jitResult); +#endif + + UString m_patternString; + RegExpFlags m_flags; + 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 000000000..cd96301db --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCache.cpp @@ -0,0 +1,87 @@ +/* + * 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" +#include "RegExpObject.h" +#include "StrongInlines.h" + +namespace JSC { + +RegExp* RegExpCache::lookupOrCreate(const UString& patternString, RegExpFlags flags) +{ + RegExpKey key(flags, patternString); + RegExpCacheMap::iterator result = m_weakCache.find(key); + if (result != m_weakCache.end()) + return result->second.get(); + RegExp* regExp = RegExp::createWithoutCaching(*m_globalData, patternString, flags); +#if ENABLE(REGEXP_TRACING) + m_globalData->addRegExpToTrace(regExp); +#endif + // We need to do a second lookup to add the RegExp as + // allocating it may have caused a gc cycle, which in + // turn may have removed items from the cache. + m_weakCache.add(key, Weak<RegExp>(*m_globalData, regExp, this)); + return regExp; +} + +RegExpCache::RegExpCache(JSGlobalData* globalData) + : m_nextEntryInStrongCache(0) + , m_globalData(globalData) +{ +} + +void RegExpCache::finalize(Handle<Unknown> handle, void*) +{ + RegExp* regExp = static_cast<RegExp*>(handle.get().asCell()); + m_weakCache.remove(regExp->key()); + regExp->invalidateCode(); +} + +void RegExpCache::addToStrongCache(RegExp* regExp) +{ + UString pattern = regExp->pattern(); + if (pattern.length() > maxStrongCacheablePatternLength) + return; + m_strongCache[m_nextEntryInStrongCache].set(*m_globalData, regExp); + m_nextEntryInStrongCache++; + if (m_nextEntryInStrongCache == maxStrongCacheableEntries) + m_nextEntryInStrongCache = 0; +} + +void RegExpCache::invalidateCode() +{ + for (int i = 0; i < maxStrongCacheableEntries; i++) + m_strongCache[i].clear(); + m_nextEntryInStrongCache = 0; + RegExpCacheMap::iterator end = m_weakCache.end(); + for (RegExpCacheMap::iterator ptr = m_weakCache.begin(); ptr != end; ++ptr) + ptr->second->invalidateCode(); +} + +} diff --git a/Source/JavaScriptCore/runtime/RegExpCache.h b/Source/JavaScriptCore/runtime/RegExpCache.h new file mode 100644 index 000000000..4f3ea1536 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCache.h @@ -0,0 +1,67 @@ +/* + * 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 "Strong.h" +#include "UString.h" +#include "Weak.h" +#include <wtf/FixedArray.h> +#include <wtf/HashMap.h> + +#ifndef RegExpCache_h +#define RegExpCache_h + +namespace JSC { + +class RegExpCache : private WeakHandleOwner { +friend class RegExp; +typedef HashMap<RegExpKey, Weak<RegExp> > RegExpCacheMap; + +public: + RegExpCache(JSGlobalData* globalData); + void invalidateCode(); + +private: + + static const unsigned maxStrongCacheablePatternLength = 256; + + static const int maxStrongCacheableEntries = 32; + + virtual void finalize(Handle<Unknown>, void* context); + + RegExp* lookupOrCreate(const UString& patternString, RegExpFlags); + void addToStrongCache(RegExp*); + RegExpCacheMap m_weakCache; // Holds all regular expressions currently live. + int m_nextEntryInStrongCache; + WTF::FixedArray<Strong<RegExp>, maxStrongCacheableEntries> m_strongCache; // Holds a select few regular expressions that have compiled and executed + JSGlobalData* m_globalData; +}; + +} // namespace JSC + +#endif // RegExpCache_h diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp new file mode 100644 index 000000000..05832ed0c --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -0,0 +1,400 @@ +/* + * 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::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::regExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) }; + +const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)}; + +/* Source for RegExpConstructor.lut.h +@begin regExpConstructorTable + input regExpConstructorInput None + $_ 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(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) + , d(adoptPtr(new RegExpConstructorPrivate)) +{ +} + +void RegExpConstructor::finishCreation(ExecState* exec, RegExpPrototype* regExpPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, "RegExp")); + ASSERT(inherits(&s_info)); + + // ECMA 15.10.5.1 RegExp.prototype + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(2), ReadOnly | DontDelete | DontEnum); +} + +void RegExpConstructor::destroy(JSCell* cell) +{ + jsCast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor(); +} + +RegExpMatchesArray::RegExpMatchesArray(ExecState* exec) + : JSArray(exec->globalData(), exec->lexicalGlobalObject()->regExpMatchesArrayStructure()) +{ +} + +void RegExpMatchesArray::finishCreation(JSGlobalData& globalData, RegExpConstructorPrivate* data) +{ + Base::finishCreation(globalData, data->lastNumSubPatterns + 1); + 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::destroy(JSCell* cell) +{ + jsCast<RegExpMatchesArray*>(cell)->RegExpMatchesArray::~RegExpMatchesArray(); +} + +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::putByIndex(this, exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); + else + JSArray::putByIndex(this, exec, i, jsUndefined()); + } + + PutPropertySlot slot; + JSArray::put(this, exec, exec->propertyNames().index, jsNumber(d->lastOvector()[0]), slot); + JSArray::put(this, exec, exec->propertyNames().input, jsString(exec, d->input), slot); + + delete d; + setSubclassData(0); +} + +JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const +{ + return RegExpMatchesArray::create(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(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), jsCast<RegExpConstructor*>(cell), propertyName, slot); +} + +bool RegExpConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticValueDescriptor<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), jsCast<RegExpConstructor*>(object), 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(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), jsCast<RegExpConstructor*>(cell), 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, JSGlobalObject* globalObject, const ArgList& args, bool callAsConstructor) +{ + JSValue arg0 = args.at(0); + JSValue arg1 = args.at(1); + + if (arg0.inherits(&RegExpObject::s_info)) { + if (!arg1.isUndefined()) + return throwError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another.")); + // If called as a function, this just returns the first argument (see 15.10.3.1). + if (callAsConstructor) { + RegExp* regExp = static_cast<RegExpObject*>(asObject(arg0))->regExp(); + return RegExpObject::create(exec, globalObject, globalObject->regExpStructure(), regExp); + } + return asObject(arg0); + } + + UString pattern = arg0.isUndefined() ? UString("") : arg0.toString(exec); + if (exec->hadException()) + return 0; + + RegExpFlags flags = NoFlags; + if (!arg1.isUndefined()) { + flags = regExpFlags(arg1.toString(exec)); + if (exec->hadException()) + return 0; + if (flags == InvalidFlags) + return throwError(exec, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor.")); + } + + RegExp* regExp = RegExp::create(exec->globalData(), pattern, flags); + if (!regExp->isValid()) + return throwError(exec, createSyntaxError(exec, regExp->errorMessage())); + return RegExpObject::create(exec, exec->lexicalGlobalObject(), globalObject->regExpStructure(), regExp); +} + +static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, true)); +} + +ConstructType RegExpConstructor::getConstructData(JSCell*, 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, asInternalFunction(exec->callee())->globalObject(), args)); +} + +CallType RegExpConstructor::getCallData(JSCell*, 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 000000000..0a43da70a --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.h @@ -0,0 +1,144 @@ +/* + * 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 { + WTF_MAKE_FAST_ALLOCATED; + public: + // Global search cache / settings + RegExpConstructorPrivate() + : lastNumSubPatterns(0) + , multiline(false) + , lastOvectorIndex(0) + { + } + + const Vector<int, 32>& lastOvector() const { return ovector[lastOvectorIndex]; } + Vector<int, 32>& lastOvector() { return ovector[lastOvectorIndex]; } + Vector<int, 32>& tempOvector() { return ovector[lastOvectorIndex ? 0 : 1]; } + void changeLastOvector() { lastOvectorIndex = lastOvectorIndex ? 0 : 1; } + + UString input; + UString lastInput; + Vector<int, 32> ovector[2]; + unsigned lastNumSubPatterns : 30; + bool multiline : 1; + unsigned lastOvectorIndex : 1; + }; + + class RegExpConstructor : public InternalFunction { + public: + typedef InternalFunction Base; + + static RegExpConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, RegExpPrototype* regExpPrototype) + { + RegExpConstructor* constructor = new (NotNull, allocateCell<RegExpConstructor>(*exec->heap())) RegExpConstructor(globalObject, structure); + constructor->finishCreation(exec, regExpPrototype); + return constructor; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static const ClassInfo s_info; + + void performMatch(JSGlobalData&, 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: + void finishCreation(ExecState*, RegExpPrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | InternalFunction::StructureFlags; + + private: + RegExpConstructor(JSGlobalObject*, Structure*); + static void destroy(JSCell*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + OwnPtr<RegExpConstructorPrivate> d; + }; + + RegExpConstructor* asRegExpConstructor(JSValue); + + JSObject* constructRegExp(ExecState*, JSGlobalObject*, const ArgList&, bool callAsConstructor = false); + + inline RegExpConstructor* asRegExpConstructor(JSValue value) + { + ASSERT(asObject(value)->inherits(&RegExpConstructor::s_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(JSGlobalData& globalData, RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) + { + position = r->match(globalData, 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 000000000..b4847f971 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpKey.h @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef RegExpKey_h +#define RegExpKey_h + +#include "UString.h" +#include <wtf/text/StringHash.h> + +namespace JSC { + +enum RegExpFlags { + NoFlags = 0, + FlagGlobal = 1, + FlagIgnoreCase = 2, + FlagMultiline = 4, + InvalidFlags = 8, + DeletedValueFlags = -1 +}; + +struct RegExpKey { + RegExpFlags flagsValue; + RefPtr<StringImpl> pattern; + + RegExpKey() + : flagsValue(NoFlags) + { + } + + RegExpKey(RegExpFlags flags) + : flagsValue(flags) + { + } + + RegExpKey(RegExpFlags flags, const UString& pattern) + : flagsValue(flags) + , pattern(pattern.impl()) + { + } + + RegExpKey(RegExpFlags flags, const PassRefPtr<StringImpl> pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + RegExpKey(RegExpFlags flags, const RefPtr<StringImpl>& pattern) + : flagsValue(flags) + , pattern(pattern) + { + } +}; + +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 = JSC::DeletedValueFlags; } + static bool isDeletedValue(const JSC::RegExpKey& value) { return value.flagsValue == JSC::DeletedValueFlags; } +}; +} // namespace WTF + +#endif // RegExpKey_h diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h new file mode 100644 index 000000000..a0a8a8e98 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -0,0 +1,123 @@ +/* + * 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 { + private: + RegExpMatchesArray(ExecState*); + + public: + typedef JSArray Base; + + static RegExpMatchesArray* create(ExecState* exec, RegExpConstructorPrivate* ctorPrivate) + { + RegExpMatchesArray* regExp = new (NotNull, allocateCell<RegExpMatchesArray>(*exec->heap())) RegExpMatchesArray(exec); + regExp->finishCreation(exec->globalData(), ctorPrivate); + return regExp; + } + ~RegExpMatchesArray(); + static void destroy(JSCell*); + + static JS_EXPORTDATA const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(JSGlobalData&, RegExpConstructorPrivate* data); + + private: + static bool getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + return JSArray::getOwnPropertySlot(thisObject, exec, propertyName, slot); + } + + static bool getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + return JSArray::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot); + } + + static bool getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + return JSArray::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); + } + + static void put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue v, PutPropertySlot& slot) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + JSArray::put(thisObject, exec, propertyName, v, slot); + } + + static void putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue v) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + JSArray::putByIndex(thisObject, exec, propertyName, v); + } + + static bool deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + return JSArray::deleteProperty(thisObject, exec, propertyName); + } + + static bool deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + return JSArray::deletePropertyByIndex(thisObject, exec, propertyName); + } + + static void getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& arr, EnumerationMode mode = ExcludeDontEnumProperties) + { + RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object); + if (thisObject->subclassData()) + thisObject->fillArrayInstance(exec); + JSArray::getOwnPropertyNames(thisObject, 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 000000000..4553f7ad0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -0,0 +1,272 @@ +/* + * 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 "Lexer.h" +#include "Lookup.h" +#include "RegExpConstructor.h" +#include "RegExpPrototype.h" +#include "UStringBuilder.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::s_info = { "RegExp", &JSNonFinalObject::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) }; + +/* 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, Structure* structure, RegExp* regExp) + : JSNonFinalObject(globalObject->globalData(), structure) + , d(adoptPtr(new RegExpObjectData(globalObject->globalData(), this, regExp))) +{ +} + +void RegExpObject::finishCreation(JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalData()); + ASSERT(inherits(&s_info)); +} + +void RegExpObject::destroy(JSCell* cell) +{ + jsCast<RegExpObject*>(cell)->RegExpObject::~RegExpObject(); +} + +void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + RegExpObject* thisObject = jsCast<RegExpObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); + if (thisObject->d->regExp) + visitor.append(&thisObject->d->regExp); + if (UNLIKELY(!thisObject->d->lastIndex.get().isInt32())) + visitor.append(&thisObject->d->lastIndex); +} + +bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), propertyName, slot); +} + +bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), 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&) +{ + UString pattern = asRegExpObject(slotBase)->regExp()->pattern(); + unsigned length = pattern.length(); + const UChar* characters = pattern.characters(); + bool previousCharacterWasBackslash = false; + bool inBrackets = false; + bool shouldEscape = false; + + // early return for strings that don't contain a forwards slash and LineTerminator + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') { + shouldEscape = true; + break; + } + if (ch == '[') + inBrackets = true; + } + } + + if (Lexer<UChar>::isLineTerminator(ch)) { + shouldEscape = true; + break; + } + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + if (!shouldEscape) + return jsString(exec, pattern); + + previousCharacterWasBackslash = false; + inBrackets = false; + UStringBuilder result; + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') + result.append('\\'); + else if (ch == '[') + inBrackets = true; + } + } + + // escape LineTerminator + if (Lexer<UChar>::isLineTerminator(ch)) { + if (!previousCharacterWasBackslash) + result.append('\\'); + + if (ch == '\n') + result.append('n'); + else if (ch == '\r') + result.append('r'); + else if (ch == 0x2028) + result.append("u2028"); + else + result.append("u2029"); + } else + result.append(ch); + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + return jsString(exec, result.toUString()); +} + +JSValue regExpObjectLastIndex(ExecState*, JSValue slotBase, const Identifier&) +{ + return asRegExpObject(slotBase)->getLastIndex(); +} + +void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot); +} + +void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue value) +{ + asRegExpObject(baseObject)->setLastIndex(exec->globalData(), value); +} + +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(); +} + +// Shared implementation used by test and exec. +bool RegExpObject::match(ExecState* exec) +{ + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + UString input = exec->argument(0).toString(exec); + JSGlobalData* globalData = &exec->globalData(); + if (!regExp()->global()) { + int position; + int length; + regExpConstructor->performMatch(*globalData, d->regExp.get(), input, 0, position, length); + return position >= 0; + } + + JSValue jsLastIndex = getLastIndex(); + unsigned lastIndex; + if (LIKELY(jsLastIndex.isUInt32())) { + lastIndex = jsLastIndex.asUInt32(); + if (lastIndex > input.length()) { + setLastIndex(0); + return false; + } + } else { + double doubleLastIndex = jsLastIndex.toInteger(exec); + if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { + setLastIndex(0); + return false; + } + lastIndex = static_cast<unsigned>(doubleLastIndex); + } + + int position; + int length = 0; + regExpConstructor->performMatch(*globalData, d->regExp.get(), input, lastIndex, position, length); + if (position < 0) { + setLastIndex(0); + return false; + } + + setLastIndex(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 000000000..4e84d3831 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.h @@ -0,0 +1,117 @@ +/* + * 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 "JSObject.h" +#include "RegExp.h" + +namespace JSC { + + class RegExpObject : public JSNonFinalObject { + public: + typedef JSNonFinalObject Base; + + static RegExpObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) + { + RegExpObject* object = new (NotNull, allocateCell<RegExpObject>(*exec->heap())) RegExpObject(globalObject, structure, regExp); + object->finishCreation(globalObject); + return object; + } + + static RegExpObject* create(JSGlobalData& globalData, JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) + { + RegExpObject* object = new (NotNull, allocateCell<RegExpObject>(globalData.heap)) RegExpObject(globalObject, structure, regExp); + object->finishCreation(globalObject); + return object; + } + + void setRegExp(JSGlobalData& globalData, RegExp* r) { d->regExp.set(globalData, this, r); } + RegExp* regExp() const { return d->regExp.get(); } + + void setLastIndex(size_t lastIndex) + { + d->lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex)); + } + void setLastIndex(JSGlobalData& globalData, JSValue lastIndex) + { + d->lastIndex.set(globalData, this, lastIndex); + } + JSValue getLastIndex() const + { + return d->lastIndex.get(); + } + + JSValue test(ExecState*); + JSValue exec(ExecState*); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + + static JS_EXPORTDATA const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + RegExpObject(JSGlobalObject*, Structure*, RegExp*); + void finishCreation(JSGlobalObject*); + static void destroy(JSCell*); + + static const unsigned StructureFlags = OverridesVisitChildren | OverridesGetOwnPropertySlot | Base::StructureFlags; + + static void visitChildren(JSCell*, SlotVisitor&); + + private: + bool match(ExecState*); + + struct RegExpObjectData { + WTF_MAKE_FAST_ALLOCATED; + public: + RegExpObjectData(JSGlobalData& globalData, RegExpObject* owner, RegExp* regExp) + : regExp(globalData, owner, regExp) + { + lastIndex.setWithoutWriteBarrier(jsNumber(0)); + } + + WriteBarrier<RegExp> regExp; + WriteBarrier<Unknown> lastIndex; + }; +#if COMPILER(MSVC) + friend void WTF::deleteOwnedPtr<RegExpObjectData>(RegExpObjectData*); +#endif + OwnPtr<RegExpObjectData> d; + }; + + RegExpObject* asRegExpObject(JSValue); + + inline RegExpObject* asRegExpObject(JSValue value) + { + ASSERT(asObject(value)->inherits(&RegExpObject::s_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 000000000..6c79f9428 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -0,0 +1,161 @@ +/* + * 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 "RegExpObject.h" +#include "RegExp.h" +#include "RegExpCache.h" +#include "StringRecursionChecker.h" +#include "UStringConcatenate.h" + +namespace JSC { + +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*); + +} + +#include "RegExpPrototype.lut.h" + +namespace JSC { + +const ClassInfo RegExpPrototype::s_info = { "RegExp", &RegExpObject::s_info, 0, ExecState::regExpPrototypeTable, CREATE_METHOD_TABLE(RegExpPrototype) }; + +/* Source for RegExpPrototype.lut.h +@begin regExpPrototypeTable + compile regExpProtoFuncCompile DontEnum|Function 2 + exec regExpProtoFuncExec DontEnum|Function 1 + test regExpProtoFuncTest DontEnum|Function 1 + toString regExpProtoFuncToString DontEnum|Function 0 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(RegExpPrototype); + +RegExpPrototype::RegExpPrototype(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) + : RegExpObject(globalObject, structure, regExp) +{ +} + +bool RegExpPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<RegExpObject>(exec, ExecState::regExpPrototypeTable(exec), jsCast<RegExpPrototype*>(cell), propertyName, slot); +} + +bool RegExpPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<RegExpObject>(exec, ExecState::regExpPrototypeTable(exec), jsCast<RegExpPrototype*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions --------------------------- + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::s_info)) + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->test(exec)); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::s_info)) + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->exec(exec)); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::s_info)) + return throwVMTypeError(exec); + + RegExp* regExp; + JSValue arg0 = exec->argument(0); + JSValue arg1 = exec->argument(1); + + if (arg0.inherits(&RegExpObject::s_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); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + RegExpFlags flags = NoFlags; + if (!arg1.isUndefined()) { + flags = regExpFlags(arg1.toString(exec)); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (flags == InvalidFlags) + return throwVMError(exec, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor.")); + } + regExp = RegExp::create(exec->globalData(), pattern, flags); + } + + if (!regExp->isValid()) + return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage())); + + asRegExpObject(thisValue)->setRegExp(exec->globalData(), regExp); + asRegExpObject(thisValue)->setLastIndex(0); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.inherits(&RegExpObject::s_info)) + return throwVMTypeError(exec); + + RegExpObject* thisObject = asRegExpObject(thisValue); + + StringRecursionChecker checker(exec, thisObject); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + char postfix[5] = { '/', 0, 0, 0, 0 }; + int index = 1; + if (thisObject->get(exec, exec->propertyNames().global).toBoolean(exec)) + postfix[index++] = 'g'; + if (thisObject->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec)) + postfix[index++] = 'i'; + if (thisObject->get(exec, exec->propertyNames().multiline).toBoolean(exec)) + postfix[index] = 'm'; + UString source = thisObject->get(exec, exec->propertyNames().source).toString(exec); + // If source is empty, use "/(?:)/" to avoid colliding with comment syntax + return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.h b/Source/JavaScriptCore/runtime/RegExpPrototype.h new file mode 100644 index 000000000..6702592fd --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.h @@ -0,0 +1,58 @@ +/* + * 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: + typedef RegExpObject Base; + + static RegExpPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) + { + RegExpPrototype* prototype = new (NotNull, allocateCell<RegExpPrototype>(*exec->heap())) RegExpPrototype(globalObject, structure, regExp); + prototype->finishCreation(globalObject); + return prototype; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + RegExpPrototype(JSGlobalObject*, Structure*, RegExp*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | RegExpObject::StructureFlags; + + private: + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + }; + +} // namespace JSC + +#endif // RegExpPrototype_h diff --git a/Source/JavaScriptCore/runtime/SamplingCounter.cpp b/Source/JavaScriptCore/runtime/SamplingCounter.cpp new file mode 100644 index 000000000..e5fb25a93 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SamplingCounter.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 "SamplingCounter.h" + +namespace JSC { + +void AbstractSamplingCounter::dump() +{ +#if ENABLE(SAMPLING_COUNTERS) + if (s_abstractSamplingCounterChain != &s_abstractSamplingCounterChainEnd) { + printf("\nSampling Counter Values:\n"); + for (AbstractSamplingCounter* currCounter = s_abstractSamplingCounterChain; (currCounter != &s_abstractSamplingCounterChainEnd); currCounter = currCounter->m_next) + printf("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); + printf("\n\n"); + } + s_completed = true; +#endif +} + +AbstractSamplingCounter AbstractSamplingCounter::s_abstractSamplingCounterChainEnd; +AbstractSamplingCounter* AbstractSamplingCounter::s_abstractSamplingCounterChain = &s_abstractSamplingCounterChainEnd; +bool AbstractSamplingCounter::s_completed = false; + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/SamplingCounter.h b/Source/JavaScriptCore/runtime/SamplingCounter.h new file mode 100644 index 000000000..664b0280e --- /dev/null +++ b/Source/JavaScriptCore/runtime/SamplingCounter.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2011 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 SamplingCounter_h +#define SamplingCounter_h + +#include <stdint.h> +#include <wtf/Assertions.h> + +namespace JSC { + +// AbstractSamplingCounter: +// +// Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS). +// See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter. +class AbstractSamplingCounter { + friend class DeletableSamplingCounter; +public: + void count(uint32_t count = 1) + { + m_counter += count; + } + + static void dump(); + + int64_t* addressOfCounter() { return &m_counter; } + +protected: + // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter. + void init(const char* name) + { + m_counter = 0; + m_name = name; + + // Set m_next to point to the head of the chain, and inform whatever is + // currently at the head that this node will now hold the pointer to it. + m_next = s_abstractSamplingCounterChain; + s_abstractSamplingCounterChain->m_referer = &m_next; + // Add this node to the head of the list. + s_abstractSamplingCounterChain = this; + m_referer = &s_abstractSamplingCounterChain; + } + + int64_t m_counter; + const char* m_name; + AbstractSamplingCounter* m_next; + // This is a pointer to the pointer to this node in the chain; used to + // allow fast linked list deletion. + AbstractSamplingCounter** m_referer; + // Null object used to detect end of static chain. + static AbstractSamplingCounter s_abstractSamplingCounterChainEnd; + static AbstractSamplingCounter* s_abstractSamplingCounterChain; + static bool s_completed; +}; + +#if ENABLE(SAMPLING_COUNTERS) +// SamplingCounter: +// +// This class is suitable and (hopefully!) convenient for cases where a counter is +// required within the scope of a single function. It can be instantiated as a +// static variable since it contains a constructor but not a destructor (static +// variables in WebKit cannot have destructors). +// +// For example: +// +// void someFunction() +// { +// static SamplingCounter countMe("This is my counter. There are many like it, but this one is mine."); +// countMe.count(); +// // ... +// } +// +class SamplingCounter : public AbstractSamplingCounter { +public: + SamplingCounter(const char* name) { init(name); } +}; + +// GlobalSamplingCounter: +// +// This class is suitable for use where a counter is to be declared globally, +// since it contains neither a constructor nor destructor. Instead, ensure +// that 'name()' is called to provide the counter with a name (and also to +// allow it to be printed out on exit). +// +// GlobalSamplingCounter globalCounter; +// +// void firstFunction() +// { +// // Put this within a function that is definitely called! +// // (Or alternatively alongside all calls to 'count()'). +// globalCounter.name("I Name You Destroyer."); +// globalCounter.count(); +// // ... +// } +// +// void secondFunction() +// { +// globalCounter.count(); +// // ... +// } +// +class GlobalSamplingCounter : public AbstractSamplingCounter { +public: + void name(const char* name) + { + // Global objects should be mapped in zero filled memory, so this should + // be a safe (albeit not necessarily threadsafe) check for 'first call'. + if (!m_next) + init(name); + } +}; + +// DeletableSamplingCounter: +// +// The above classes (SamplingCounter, GlobalSamplingCounter), are intended for +// use within a global or static scope, and as such cannot have a destructor. +// This means there is no convenient way for them to remove themselves from the +// static list of counters, and should an instance of either class be freed +// before 'dump()' has walked over the list it will potentially walk over an +// invalid pointer. +// +// This class is intended for use where the counter may possibly be deleted before +// the program exits. Should this occur, the counter will print it's value to +// stderr, and remove itself from the static list. Example: +// +// DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name"); +// counter->count(); +// delete counter; +// +class DeletableSamplingCounter : public AbstractSamplingCounter { +public: + DeletableSamplingCounter(const char* name) { init(name); } + + ~DeletableSamplingCounter() + { + if (!s_completed) + fprintf(stderr, "DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter); + // Our m_referer pointer should know where the pointer to this node is, + // and m_next should know that this node is the previous node in the list. + ASSERT(*m_referer == this); + ASSERT(m_next->m_referer == &m_next); + // Remove this node from the list, and inform m_next that we have done so. + m_next->m_referer = m_referer; + *m_referer = m_next; + } +}; +#endif + +} // namespace JSC + +#endif // SamplingCounter_h + + diff --git a/Source/JavaScriptCore/runtime/ScopeChain.cpp b/Source/JavaScriptCore/runtime/ScopeChain.cpp new file mode 100644 index 000000000..099f7fde6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeChain.cpp @@ -0,0 +1,85 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ScopeChainNode); + +#ifndef NDEBUG + +void ScopeChainNode::print() +{ + ScopeChainIterator scopeEnd = end(); + for (ScopeChainIterator scopeIter = begin(); scopeIter != scopeEnd; ++scopeIter) { + JSObject* o = scopeIter->get(); + PropertyNameArray propertyNames(globalObject->globalExec()); + o->methodTable()->getPropertyNames(o, globalObject->globalExec(), propertyNames, ExcludeDontEnumProperties); + 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 + +const ClassInfo ScopeChainNode::s_info = { "ScopeChainNode", 0, 0, 0, CREATE_METHOD_TABLE(ScopeChainNode) }; + +int ScopeChainNode::localDepth() +{ + int scopeDepth = 0; + ScopeChainIterator iter = this->begin(); + ScopeChainIterator end = this->end(); + while (!(*iter)->inherits(&JSActivation::s_info)) { + ++iter; + if (iter == end) + break; + ++scopeDepth; + } + return scopeDepth; +} + +void ScopeChainNode::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + ScopeChainNode* thisObject = jsCast<ScopeChainNode*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + if (thisObject->next) + visitor.append(&thisObject->next); + visitor.append(&thisObject->object); + visitor.append(&thisObject->globalObject); + visitor.append(&thisObject->globalThis); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ScopeChain.h b/Source/JavaScriptCore/runtime/ScopeChain.h new file mode 100644 index 000000000..6e358d779 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeChain.h @@ -0,0 +1,169 @@ +/* + * 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 "JSCell.h" +#include "Structure.h" +#include <wtf/FastAllocBase.h> + +namespace JSC { + + class JSGlobalData; + class JSGlobalObject; + class JSObject; + class ScopeChainIterator; + class SlotVisitor; + + class ScopeChainNode : public JSCell { + private: + ScopeChainNode(ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) + : JSCell(*globalData, globalData->scopeChainNodeStructure.get()) + , globalData(globalData) + , next(*globalData, this, next, WriteBarrier<ScopeChainNode>::MayBeNull) + , object(*globalData, this, object) + , globalObject(*globalData, this, globalObject) + , globalThis(*globalData, this, globalThis) + { + } + + protected: + void finishCreation(JSGlobalData* globalData, JSGlobalObject* globalObject) + { + Base::finishCreation(*globalData); + ASSERT_UNUSED(globalObject, globalObject); + } + + public: + typedef JSCell Base; + + static ScopeChainNode* create(ExecState* exec, ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) + { + ScopeChainNode* node = new (NotNull, allocateCell<ScopeChainNode>(*exec->heap())) ScopeChainNode(next, object, globalData, globalObject, globalThis); + node->finishCreation(globalData, globalObject); + return node; + } + static ScopeChainNode* create(ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) + { + ScopeChainNode* node = new (NotNull, allocateCell<ScopeChainNode>(globalData->heap)) ScopeChainNode(next, object, globalData, globalObject, globalThis); + node->finishCreation(globalData, globalObject); + return node; + } + + JSGlobalData* globalData; + WriteBarrier<ScopeChainNode> next; + WriteBarrier<JSObject> object; + WriteBarrier<JSGlobalObject> globalObject; + WriteBarrier<JSObject> globalThis; + + ScopeChainNode* push(JSObject*); + ScopeChainNode* pop(); + + ScopeChainIterator begin(); + ScopeChainIterator end(); + + int localDepth(); + +#ifndef NDEBUG + void print(); +#endif + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(CompoundType, StructureFlags), &s_info); } + static void visitChildren(JSCell*, SlotVisitor&); + static JS_EXPORTDATA const ClassInfo s_info; + + private: + static const unsigned StructureFlags = OverridesVisitChildren; + }; + + inline ScopeChainNode* ScopeChainNode::push(JSObject* o) + { + ASSERT(o); + return ScopeChainNode::create(this, o, globalData, globalObject.get(), globalThis.get()); + } + + inline ScopeChainNode* ScopeChainNode::pop() + { + ASSERT(next); + return next.get(); + } + + class ScopeChainIterator { + public: + ScopeChainIterator(ScopeChainNode* node) + : m_node(node) + { + } + + WriteBarrier<JSObject> const & operator*() const { return m_node->object; } + WriteBarrier<JSObject> const * operator->() const { return &(operator*()); } + + ScopeChainIterator& operator++() { m_node = m_node->next.get(); 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: + ScopeChainNode* m_node; + }; + + inline ScopeChainIterator ScopeChainNode::begin() + { + return ScopeChainIterator(this); + } + + inline ScopeChainIterator ScopeChainNode::end() + { + return ScopeChainIterator(0); + } + + ALWAYS_INLINE JSGlobalData& ExecState::globalData() const + { + ASSERT(scopeChain()->globalData); + return *scopeChain()->globalData; + } + + ALWAYS_INLINE JSGlobalObject* ExecState::lexicalGlobalObject() const + { + return scopeChain()->globalObject.get(); + } + + ALWAYS_INLINE JSObject* ExecState::globalThisValue() const + { + return scopeChain()->globalThis.get(); + } + + ALWAYS_INLINE ScopeChainNode* Register::scopeChain() const + { + return static_cast<ScopeChainNode*>(jsValue().asCell()); + } + + ALWAYS_INLINE Register& Register::operator=(ScopeChainNode* scopeChain) + { + *this = JSValue(scopeChain); + 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 000000000..35701f11d --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeChainMark.h @@ -0,0 +1,30 @@ +/* + * 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 { + +} // namespace JSC + +#endif // ScopeChainMark_h diff --git a/Source/JavaScriptCore/runtime/SmallStrings.cpp b/Source/JavaScriptCore/runtime/SmallStrings.cpp new file mode 100644 index 000000000..caf201c3d --- /dev/null +++ b/Source/JavaScriptCore/runtime/SmallStrings.cpp @@ -0,0 +1,127 @@ +/* + * 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 "HeapRootVisitor.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +static inline void finalize(JSString*& string) +{ + if (!string || Heap::isMarked(string)) + return; + string = 0; +} + +class SmallStringsStorage { + WTF_MAKE_NONCOPYABLE(SmallStringsStorage); WTF_MAKE_FAST_ALLOCATED; +public: + SmallStringsStorage(); + + StringImpl* rep(unsigned char character) + { + return m_reps[character].get(); + } + +private: + static const unsigned singleCharacterStringCount = maxSingleCharacterString + 1; + + RefPtr<StringImpl> m_reps[singleCharacterStringCount]; +}; + +SmallStringsStorage::SmallStringsStorage() +{ + LChar* characterBuffer = 0; + RefPtr<StringImpl> baseString = StringImpl::createUninitialized(singleCharacterStringCount, characterBuffer); + for (unsigned i = 0; i < singleCharacterStringCount; ++i) { + characterBuffer[i] = i; + m_reps[i] = StringImpl::create(baseString, i, 1); + } +} + +SmallStrings::SmallStrings() +{ + COMPILE_ASSERT(singleCharacterStringCount == sizeof(m_singleCharacterStrings) / sizeof(m_singleCharacterStrings[0]), IsNumCharactersConstInSyncWithClassUsage); + clear(); +} + +SmallStrings::~SmallStrings() +{ +} + +void SmallStrings::finalizeSmallStrings() +{ + finalize(m_emptyString); + for (unsigned i = 0; i < singleCharacterStringCount; ++i) + finalize(m_singleCharacterStrings[i]); +} + +void SmallStrings::clear() +{ + m_emptyString = 0; + for (unsigned i = 0; i < singleCharacterStringCount; ++i) + m_singleCharacterStrings[i] = 0; +} + +unsigned SmallStrings::count() const +{ + unsigned count = 0; + if (m_emptyString) + ++count; + for (unsigned i = 0; i < singleCharacterStringCount; ++i) { + if (m_singleCharacterStrings[i]) + ++count; + } + return count; +} + +void SmallStrings::createEmptyString(JSGlobalData* globalData) +{ + ASSERT(!m_emptyString); + m_emptyString = JSString::createHasOtherOwner(*globalData, StringImpl::empty()); +} + +void SmallStrings::createSingleCharacterString(JSGlobalData* globalData, unsigned char character) +{ + if (!m_storage) + m_storage = adoptPtr(new SmallStringsStorage); + ASSERT(!m_singleCharacterStrings[character]); + m_singleCharacterStrings[character] = JSString::createHasOtherOwner(*globalData, PassRefPtr<StringImpl>(m_storage->rep(character))); +} + +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 000000000..9c6ed9a32 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SmallStrings.h @@ -0,0 +1,85 @@ +/* + * 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 HeapRootVisitor; + class JSGlobalData; + class JSString; + class SmallStringsStorage; + class SlotVisitor; + + static const unsigned maxSingleCharacterString = 0xFF; + + class SmallStrings { + WTF_MAKE_NONCOPYABLE(SmallStrings); + 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 finalizeSmallStrings(); + void clear(); + + unsigned count() const; + + JSString** singleCharacterStrings() { return &m_singleCharacterStrings[0]; } + + private: + static const unsigned singleCharacterStringCount = maxSingleCharacterString + 1; + + void createEmptyString(JSGlobalData*); + void createSingleCharacterString(JSGlobalData*, unsigned char); + + JSString* m_emptyString; + JSString* m_singleCharacterStrings[singleCharacterStringCount]; + OwnPtr<SmallStringsStorage> m_storage; + }; + +} // namespace JSC + +#endif // SmallStrings_h diff --git a/Source/JavaScriptCore/runtime/StorageBarrier.h b/Source/JavaScriptCore/runtime/StorageBarrier.h new file mode 100644 index 000000000..2a0c842ba --- /dev/null +++ b/Source/JavaScriptCore/runtime/StorageBarrier.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 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 StorageBarrier_h +#define StorageBarrier_h + +#include "JSGlobalData.h" +#include "WriteBarrier.h" + +namespace JSC { + +typedef WriteBarrierBase<Unknown>* PropertyStorage; +typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage; + +class StorageBarrier { +public: + enum UncheckedTag { Unchecked }; + StorageBarrier(JSGlobalData& globalData, JSCell* owner, PropertyStorage storage) + { + set(globalData, owner, storage); + } + + StorageBarrier(PropertyStorage storage, UncheckedTag) + { + set(storage, Unchecked); + } + + void set(JSGlobalData&, JSCell*, PropertyStorage newStorage) + { + m_storage = newStorage; + } + + void set(PropertyStorage newStorage, UncheckedTag) + { + m_storage = newStorage; + } + + WriteBarrierBase<Unknown>* operator->() const { return m_storage; } + WriteBarrierBase<Unknown>* operator->() { return m_storage; } + WriteBarrierBase<Unknown> operator*() const { return *m_storage; } + WriteBarrierBase<Unknown> operator*() { return *m_storage; } + const WriteBarrierBase<Unknown>& operator[](size_t i) const { return m_storage[i]; } + WriteBarrierBase<Unknown>& operator[](size_t i) { return m_storage[i]; } + + ConstPropertyStorage get() const { return m_storage; } + PropertyStorage get() { return m_storage; } + +private: + PropertyStorage m_storage; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp b/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp new file mode 100644 index 000000000..fbd9d5fbd --- /dev/null +++ b/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp @@ -0,0 +1,50 @@ +/* + * 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 { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(StrictEvalActivation); + +const ClassInfo StrictEvalActivation::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(StrictEvalActivation) }; + +StrictEvalActivation::StrictEvalActivation(ExecState* exec) + : JSNonFinalObject(exec->globalData(), exec->globalData().strictEvalActivationStructure.get()) +{ +} + +bool StrictEvalActivation::deleteProperty(JSCell*, ExecState*, const Identifier&) +{ + return false; +} + +JSObject* StrictEvalActivation::toThisObject(JSCell*, ExecState* exec) +{ + return exec->globalThisValue(); +} + +} diff --git a/Source/JavaScriptCore/runtime/StrictEvalActivation.h b/Source/JavaScriptCore/runtime/StrictEvalActivation.h new file mode 100644 index 000000000..a7b6d3e96 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StrictEvalActivation.h @@ -0,0 +1,63 @@ +/* + * 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 JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static StrictEvalActivation* create(ExecState* exec) + { + StrictEvalActivation* activation = new (NotNull, allocateCell<StrictEvalActivation>(*exec->heap())) StrictEvalActivation(exec); + activation->finishCreation(exec->globalData()); + return activation; + } + + static bool deleteProperty(JSCell*, ExecState*, const Identifier&); + static JSObject* toThisObject(JSCell*, ExecState*); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + +protected: + static const unsigned StructureFlags = IsEnvironmentRecord | JSNonFinalObject::StructureFlags; + +private: + StrictEvalActivation(ExecState*); +}; + +} // namespace JSC + +#endif // StrictEvalActivation_h diff --git a/Source/JavaScriptCore/runtime/StringConstructor.cpp b/Source/JavaScriptCore/runtime/StringConstructor.cpp new file mode 100644 index 000000000..d2f75a060 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringConstructor.cpp @@ -0,0 +1,123 @@ +/* + * 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 "StringPrototype.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL stringFromCharCode(ExecState*); + +} + +#include "StringConstructor.lut.h" + +namespace JSC { + +const ClassInfo StringConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::stringConstructorTable, CREATE_METHOD_TABLE(StringConstructor) }; + +/* Source for StringConstructor.lut.h +@begin stringConstructorTable + fromCharCode stringFromCharCode DontEnum|Function 1 +@end +*/ + +ASSERT_CLASS_FITS_IN_CELL(StringConstructor); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(StringConstructor); + +StringConstructor::StringConstructor(JSGlobalObject* globalObject, Structure* structure) + : InternalFunction(globalObject, structure) +{ +} + +void StringConstructor::finishCreation(ExecState* exec, StringPrototype* stringPrototype) +{ + Base::finishCreation(exec->globalData(), Identifier(exec, stringPrototype->classInfo()->className)); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, stringPrototype, ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool StringConstructor::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, ExecState::stringConstructorTable(exec), jsCast<StringConstructor*>(cell), propertyName, slot); +} + +bool StringConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<InternalFunction>(exec, ExecState::stringConstructorTable(exec), jsCast<StringConstructor*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions -------------------------------- + +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)); +} + +static EncodedJSValue JSC_HOST_CALL constructWithStringConstructor(ExecState* exec) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + if (!exec->argumentCount()) + return JSValue::encode(StringObject::create(exec, globalObject->stringObjectStructure())); + + JSString* string = exec->argument(0).isString() + ? asString(exec->argument(0)) + : jsString(exec, exec->argument(0).toString(exec)); + return JSValue::encode(StringObject::create(exec, globalObject->stringObjectStructure(), string)); +} + +ConstructType StringConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithStringConstructor; + return ConstructTypeHost; +} + +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(JSCell*, 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 000000000..6f553cdbb --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringConstructor.h @@ -0,0 +1,63 @@ +/* + * 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: + typedef InternalFunction Base; + + static StringConstructor* create(ExecState* exec, JSGlobalObject* globalObject , Structure* structure, StringPrototype* stringPrototype) + { + StringConstructor* constructor = new (NotNull, allocateCell<StringConstructor>(*exec->heap())) StringConstructor(globalObject, structure); + constructor->finishCreation(exec, stringPrototype); + return constructor; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; + + private: + StringConstructor(JSGlobalObject*, Structure*); + void finishCreation(ExecState*, StringPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + }; + +} // namespace JSC + +#endif // StringConstructor_h diff --git a/Source/JavaScriptCore/runtime/StringObject.cpp b/Source/JavaScriptCore/runtime/StringObject.cpp new file mode 100644 index 000000000..4a24698fb --- /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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(StringObject); + +const ClassInfo StringObject::s_info = { "String", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(StringObject) }; + +StringObject::StringObject(JSGlobalData& globalData, Structure* structure) + : JSWrapperObject(globalData, structure) +{ +} + +void StringObject::finishCreation(JSGlobalData& globalData, JSString* string) +{ + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + setInternalValue(globalData, string); +} + +bool StringObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (thisObject->internalValue()->getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +bool StringObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (thisObject->internalValue()->getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); +} + +bool StringObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + StringObject* thisObject = jsCast<StringObject*>(object); + if (thisObject->internalValue()->getStringPropertyDescriptor(exec, propertyName, descriptor)) + return true; + return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); +} + +void StringObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) + return; + JSObject::put(cell, exec, propertyName, value, slot); +} + +bool StringObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (propertyName == exec->propertyNames().length) + return false; + bool isStrictUInt32; + unsigned i = propertyName.toUInt32(isStrictUInt32); + if (isStrictUInt32 && thisObject->internalValue()->canGetIndex(i)) + return false; + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +void StringObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + StringObject* thisObject = jsCast<StringObject*>(object); + int size = thisObject->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(thisObject, exec, propertyNames, mode); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringObject.h b/Source/JavaScriptCore/runtime/StringObject.h new file mode 100644 index 000000000..528668691 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringObject.h @@ -0,0 +1,82 @@ +/* + * 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: + typedef JSWrapperObject Base; + + static StringObject* create(ExecState* exec, Structure* structure) + { + JSString* string = jsEmptyString(exec); + StringObject* object = new (NotNull, allocateCell<StringObject>(*exec->heap())) StringObject(exec->globalData(), structure); + object->finishCreation(exec->globalData(), string); + return object; + } + static StringObject* create(ExecState* exec, Structure* structure, JSString* string) + { + StringObject* object = new (NotNull, allocateCell<StringObject>(*exec->heap())) StringObject(exec->globalData(), structure); + object->finishCreation(exec->globalData(), string); + return object; + } + static StringObject* create(ExecState*, JSGlobalObject*, JSString*); + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + + static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static const JS_EXPORTDATA ClassInfo s_info; + + JSString* internalValue() const { return asString(JSWrapperObject::internalValue());} + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + void finishCreation(JSGlobalData&, JSString*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSWrapperObject::StructureFlags; + StringObject(JSGlobalData&, Structure*); + }; + + StringObject* asStringObject(JSValue); + + inline StringObject* asStringObject(JSValue value) + { + ASSERT(asObject(value)->inherits(&StringObject::s_info)); + return static_cast<StringObject*>(asObject(value)); + } + +} // namespace JSC + +#endif // StringObject_h diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp new file mode 100644 index 000000000..740916a69 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -0,0 +1,1490 @@ +/* + * 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); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(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::s_info = { "String", &StringObject::s_info, 0, ExecState::stringTable, CREATE_METHOD_TABLE(StringPrototype) }; + +/* 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, Structure* structure) + : StringObject(exec->globalData(), structure) +{ +} + +void StringPrototype::finishCreation(ExecState* exec, JSGlobalObject*, JSString* nameAndMessage) +{ + Base::finishCreation(exec->globalData(), nameAndMessage); + ASSERT(inherits(&s_info)); + + // The constructor will be added later, after StringConstructor has been built + putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +bool StringPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), jsCast<StringPrototype*>(cell), propertyName, slot); +} + +bool StringPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + return getStaticFunctionDescriptor<StringObject>(exec, ExecState::stringTable(exec), jsCast<StringPrototype*>(object), propertyName, descriptor); +} + +// ------------------------------ Functions -------------------------- + +// Helper for producing a JSString for 'string', where 'string' was been produced by +// calling ToString on 'originalValue'. In cases where 'originalValue' already was a +// string primitive we can just use this, otherwise we need to allocate a new JSString. +static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const UString& string) +{ + if (originalValue.isString()) { + ASSERT(asString(originalValue)->value(exec) == string); + return asString(originalValue); + } + return jsString(exec, string); +} + +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 jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount) +{ + if (rangeCount == 1) { + 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; + + if (!totalLength) + return jsString(exec, ""); + + if (source.is8Bit()) { + LChar* buffer; + const LChar* sourceData = source.characters8(); + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int bufferPos = 0; + for (int i = 0; i < rangeCount; i++) { + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + + return jsString(exec, impl.release()); + } + + UChar* buffer; + const UChar* sourceData = source.characters16(); + + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int bufferPos = 0; + for (int i = 0; i < rangeCount; i++) { + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + + return jsString(exec, impl.release()); +} + +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; + bool allSeperators8Bit = true; + for (int i = 0; i < rangeCount; i++) + totalLength += substringRanges[i].length; + for (int i = 0; i < separatorCount; i++) { + totalLength += separators[i].length(); + if (separators[i].length() && !separators[i].is8Bit()) + allSeperators8Bit = false; + } + + if (!totalLength) + return jsString(exec, ""); + + if (source.is8Bit() && allSeperators8Bit) { + LChar* buffer; + const LChar* sourceData = source.characters8(); + + RefPtr<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, sourceData + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + if (i < separatorCount) { + if (int sepLen = separators[i].length()) { + StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); + bufferPos += sepLen; + } + } + } + + return jsString(exec, impl.release()); + } + + UChar* buffer; + RefPtr<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.release()); +} + +static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue) +{ + UString replacementString; + CallData callData; + CallType callType = getCallData(replaceValue, callData); + if (callType == CallTypeNone) + replacementString = replaceValue.toString(exec); + + const UString& source = string->value(exec); + unsigned sourceLen = source.length(); + if (exec->hadException()) + return JSValue::encode(JSValue()); + RegExp* regExp = asRegExpObject(searchValue)->regExp(); + bool global = regExp->global(); + + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + + // Optimization for substring removal (replace with empty). + if (global && callType == CallTypeNone && !replacementString.length()) { + int lastIndex = 0; + unsigned startPosition = 0; + + Vector<StringRange, 16> sourceRanges; + JSGlobalData* globalData = &exec->globalData(); + while (true) { + int matchIndex; + int matchLen = 0; + int* ovector; + regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + if (lastIndex < matchIndex) + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + lastIndex = matchIndex + matchLen; + startPosition = lastIndex; + + // special case of empty match + if (!matchLen) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + + if (!lastIndex) + return JSValue::encode(string); + + if (static_cast<unsigned>(lastIndex) < sourceLen) + sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); + + return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size())); + } + + 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) { + // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string + int argCount = regExp->numSubpatterns() + 1 + 2; + JSFunction* func = asFunction(replaceValue); + CachedCall cachedCall(exec, func, argCount); + if (exec->hadException()) + return JSValue::encode(jsNull()); + JSGlobalData* globalData = &exec->globalData(); + if (source.is8Bit()) { + while (true) { + int matchIndex; + int matchLen = 0; + int* ovector; + regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + int completeMatchStart = ovector[0]; + unsigned i = 0; + for (; i < regExp->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, jsSubstring8(globalData, source, matchStart, matchLen)); + } + + cachedCall.setArgument(i++, jsNumber(completeMatchStart)); + cachedCall.setArgument(i++, string); + + cachedCall.setThis(jsUndefined()); + 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) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + } else { + while (true) { + int matchIndex; + int matchLen = 0; + int* ovector; + regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + int completeMatchStart = ovector[0]; + unsigned i = 0; + for (; i < regExp->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(globalData, source, matchStart, matchLen)); + } + + cachedCall.setArgument(i++, jsNumber(completeMatchStart)); + cachedCall.setArgument(i++, string); + + cachedCall.setThis(jsUndefined()); + 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) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + } + } else { + JSGlobalData* globalData = &exec->globalData(); + do { + int matchIndex; + int matchLen = 0; + int* ovector; + regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + if (callType != CallTypeNone) { + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + int completeMatchStart = ovector[0]; + MarkedArgumentBuffer args; + + for (unsigned i = 0; i < regExp->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(string); + + replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), 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, regExp)); + else + replacements.append(UString()); + } + } + + lastIndex = matchIndex + matchLen; + startPosition = lastIndex; + + // special case of empty match + if (!matchLen) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } while (global); + } + + if (!lastIndex && replacements.isEmpty()) + return JSValue::encode(string); + + if (static_cast<unsigned>(lastIndex) < sourceLen) + sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); + + return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); +} + +static NEVER_INLINE EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue) +{ + const UString& string = jsString->value(exec); + UString searchString = searchValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + size_t matchStart = string.find(searchString); + if (matchStart == notFound) + return JSValue::encode(jsString); + + CallData callData; + CallType callType = getCallData(replaceValue, callData); + if (callType != CallTypeNone) { + MarkedArgumentBuffer args; + args.append(jsSubstring(exec, string, matchStart, searchString.length())); + args.append(jsNumber(matchStart)); + args.append(jsString); + replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + UString replaceString = replaceValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + size_t matchEnd = matchStart + searchString.length(); + int ovector[2] = { matchStart, matchEnd}; + return JSValue::encode(JSC::jsString(exec, string.substringSharingImpl(0, matchStart), substituteBackreferences(replaceString, string, ovector, 0), string.substringSharingImpl(matchEnd))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (!thisValue.isString()) { + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + thisValue = jsString(exec, thisValue.toString(exec)); + } + JSString* string = asString(thisValue); + JSValue searchValue = exec->argument(0); + JSValue replaceValue = exec->argument(1); + + if (searchValue.inherits(&RegExpObject::s_info)) + return replaceUsingRegExpSearch(exec, string, searchValue, replaceValue); + return replaceUsingStringSearch(exec, string, searchValue, replaceValue); +} + +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::s_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.toString(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.toString(exec); + unsigned len = s.length(); + JSValue a0 = exec->argument(0); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); + if (i < len) { + if (s.is8Bit()) + return JSValue::encode(jsNumber(s.characters8()[i])); + return JSValue::encode(jsNumber(s.characters16()[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), jsString(&exec->globalData(), v.toString(exec)))); + } + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + return JSValue::encode(jsStringFromArguments(exec, thisValue)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(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.toString(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; + + 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.toString(exec); + JSGlobalData* globalData = &exec->globalData(); + + JSValue a0 = exec->argument(0); + + RegExp* reg; + if (a0.inherits(&RegExpObject::s_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). + * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string. + */ + reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec), NoFlags); + } + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + int pos; + int matchLength = 0; + regExpConstructor->performMatch(*globalData, reg, s, 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; + while (pos >= 0) { + list.append(jsSubstring(exec, s, pos, matchLength)); + pos += matchLength == 0 ? 1 : matchLength; + regExpConstructor->performMatch(*globalData, reg, s, pos, pos, matchLength); + } + 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.toString(exec); + JSGlobalData* globalData = &exec->globalData(); + + JSValue a0 = exec->argument(0); + + RegExp* reg; + if (a0.inherits(&RegExpObject::s_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). + * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string. + */ + reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec), NoFlags); + } + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + int pos; + int matchLength = 0; + regExpConstructor->performMatch(*globalData, reg, s, 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.toString(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)); +} + +// ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) +{ + // 1. Call CheckObjectCoercible passing the this value as its argument. + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) + return throwVMTypeError(exec); + + // 2. Let S be the result of calling ToString, giving it the this value as its argument. + // 6. Let s be the number of characters in S. + UString input = thisValue.toString(exec); + + // 3. Let A be a new array created as if by the expression new Array() + // where Array is the standard built-in constructor with that name. + JSArray* result = constructEmptyArray(exec); + + // 4. Let lengthA be 0. + unsigned resultLength = 0; + + // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit). + JSValue limitValue = exec->argument(1); + unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec); + + // 7. Let p = 0. + size_t position = 0; + + // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator; + // otherwise let R = ToString(separator). + JSValue separatorValue = exec->argument(0); + if (separatorValue.inherits(&RegExpObject::s_info)) { + JSGlobalData* globalData = &exec->globalData(); + RegExp* reg = asRegExpObject(separatorValue)->regExp(); + + // 9. If lim == 0, return A. + if (!limit) + return JSValue::encode(result); + + // 10. If separator is undefined, then + if (separatorValue.isUndefined()) { + // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + // b. Return A. + return JSValue::encode(result); + } + + // 11. If s == 0, then + if (input.isEmpty()) { + // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result. + // b. If z is not failure, return A. + // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + // d. Return A. + if (reg->match(*globalData, input, 0) < 0) + result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + return JSValue::encode(result); + } + + // 12. Let q = p. + size_t matchPosition = 0; + // 13. Repeat, while q != s + while (matchPosition < input.length()) { + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + Vector<int, 32> ovector; + int mpos = reg->match(*globalData, input, matchPosition, &ovector); + // b. If z is failure, then let q = q + 1. + if (mpos < 0) + break; + matchPosition = mpos; + + // c. Else, z is not failure + // i. z must be a State. Let e be z's endIndex and let cap be z's captures array. + size_t matchEnd = ovector[1]; + + // ii. If e == p, then let q = q + 1. + if (matchEnd == position) { + ++matchPosition; + continue; + } + // iii. Else, e != p + + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position)); + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); + + // 5. Let p = e. + // 8. Let q = p. + position = matchEnd; + matchPosition = matchEnd; + + // 6. Let i = 0. + // 7. Repeat, while i is not equal to the number of elements in cap. + // a Let i = i + 1. + for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) { + // b Call the [[DefineOwnProperty]] internal method of A with arguments + // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]: + // true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + int sub = ovector[i * 2]; + result->methodTable()->putByIndex(result, exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub)); + // c Increment lengthA by 1. + // d If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); + } + } + } else { + UString separator = separatorValue.toString(exec); + + // 9. If lim == 0, return A. + if (!limit) + return JSValue::encode(result); + + // 10. If separator is undefined, then + JSValue separatorValue = exec->argument(0); + if (separatorValue.isUndefined()) { + // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + // b. Return A. + return JSValue::encode(result); + } + + // 11. If s == 0, then + if (input.isEmpty()) { + // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result. + // b. If z is not failure, return A. + // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + // d. Return A. + if (!separator.isEmpty()) + result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input)); + return JSValue::encode(result); + } + + // Optimized case for splitting on the empty string. + if (separator.isEmpty()) { + limit = std::min(limit, input.length()); + // Zero limt/input length handled in steps 9/11 respectively, above. + ASSERT(limit); + + do { + result->methodTable()->putByIndex(result, exec, position, jsSingleCharacterSubstring(exec, input, position)); + } while (++position < limit); + + return JSValue::encode(result); + } + + // 12. Let q = p. + size_t matchPosition; + // 13. Repeat, while q != s + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + // b. If z is failure, then let q = q+1. + // c. Else, z is not failure + while ((matchPosition = input.find(separator, position)) != notFound) { + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position)); + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); + + // 5. Let p = e. + // 8. Let q = p. + position = matchPosition + separator.length(); + } + } + + // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through s (exclusive). + // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor + // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->methodTable()->putByIndex(result, exec, resultLength++, jsSubstring(exec, input, position, input.length() - position)); + + // 16. Return A. + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + unsigned len; + JSString* jsString = 0; + UString uString; + if (thisValue.isString()) { + jsString = static_cast<JSString*>(thisValue.asCell()); + len = jsString->length(); + } else if (thisValue.isUndefinedOrNull()) { + // CheckObjectCoercible + return throwVMTypeError(exec); + } else { + uString = thisValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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(); + int len; + JSString* jsString = 0; + UString uString; + if (thisValue.isString()) { + jsString = static_cast<JSString*>(thisValue.asCell()); + len = jsString->length(); + } else if (thisValue.isUndefinedOrNull()) { + // CheckObjectCoercible + return throwVMTypeError(exec); + } else { + uString = thisValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + 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.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec)); + const UString& s = sVal->value(exec); + + int sSize = s.length(); + if (!sSize) + return JSValue::encode(sVal); + + StringImpl* ourImpl = s.impl(); + RefPtr<StringImpl> lower = ourImpl->lower(); + if (ourImpl == lower.get()) + return JSValue::encode(sVal); + return JSValue::encode(jsString(exec, UString(lower.release()))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + JSString* sVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(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.toString(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(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(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(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(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(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(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(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + UString s = thisValue.toString(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.toString(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 000000000..113f21f2a --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringPrototype.h @@ -0,0 +1,63 @@ +/* + * 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 { + private: + StringPrototype(ExecState*, Structure*); + + public: + typedef StringObject Base; + + static StringPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + JSString* empty = jsEmptyString(exec); + StringPrototype* prototype = new (NotNull, allocateCell<StringPrototype>(*exec->heap())) StringPrototype(exec, structure); + prototype->finishCreation(exec, globalObject, empty); + return prototype; + } + + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + static const ClassInfo s_info; + + protected: + void finishCreation(ExecState*, JSGlobalObject*, JSString*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | StringObject::StructureFlags; + + }; + +} // namespace JSC + +#endif // StringPrototype_h diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp b/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp new file mode 100644 index 000000000..22b7367bc --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 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 "StringRecursionChecker.h" + +#include "Error.h" +#include "ExceptionHelpers.h" + +namespace JSC { + +JSValue StringRecursionChecker::throwStackOverflowError() +{ + return throwError(m_exec, createStackOverflowError(m_exec)); +} + +JSValue StringRecursionChecker::emptyString() +{ + return jsEmptyString(m_exec); +} + +} diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.h b/Source/JavaScriptCore/runtime/StringRecursionChecker.h new file mode 100644 index 000000000..e3408b08b --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringRecursionChecker.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 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 StringRecursionChecker_h +#define StringRecursionChecker_h + +#include "Interpreter.h" + +namespace JSC { + +class StringRecursionChecker { + WTF_MAKE_NONCOPYABLE(StringRecursionChecker); + +public: + StringRecursionChecker(ExecState*, JSObject* thisObject); + ~StringRecursionChecker(); + + JSValue earlyReturnValue() const; // 0 if everything is OK, value to return for failure cases + +private: + JSValue throwStackOverflowError(); + JSValue emptyString(); + JSValue performCheck(); + + ExecState* m_exec; + JSObject* m_thisObject; + JSValue m_earlyReturnValue; +}; + +inline JSValue StringRecursionChecker::performCheck() +{ + int size = m_exec->globalData().stringRecursionCheckVisitedObjects.size(); + if (size >= MaxSmallThreadReentryDepth && size >= m_exec->globalData().maxReentryDepth) + return throwStackOverflowError(); + bool alreadyVisited = !m_exec->globalData().stringRecursionCheckVisitedObjects.add(m_thisObject).second; + if (alreadyVisited) + return emptyString(); // Return empty string to avoid infinite recursion. + return JSValue(); // Indicate success. +} + +inline StringRecursionChecker::StringRecursionChecker(ExecState* exec, JSObject* thisObject) + : m_exec(exec) + , m_thisObject(thisObject) + , m_earlyReturnValue(performCheck()) +{ +} + +inline JSValue StringRecursionChecker::earlyReturnValue() const +{ + return m_earlyReturnValue; +} + +inline StringRecursionChecker::~StringRecursionChecker() +{ + if (m_earlyReturnValue) + return; + ASSERT(m_exec->globalData().stringRecursionCheckVisitedObjects.contains(m_thisObject)); + m_exec->globalData().stringRecursionCheckVisitedObjects.remove(m_thisObject); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp new file mode 100644 index 000000000..dda8c73e9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -0,0 +1,851 @@ +/* + * 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> +#include <wtf/Threading.h> + +#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; + +#if DUMP_PROPERTYMAP_STATS + +int numProbes; +int numCollisions; +int numRehashes; +int numRemoves; + +#endif + +namespace JSC { + +#if DUMP_STRUCTURE_ID_STATISTICS +static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); +#endif + +bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const +{ + if (isUsingSingleSlot()) { + Structure* transition = singleTransition(); + return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes; + } + return map()->contains(make_pair(rep, attributes)); +} + +inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const +{ + if (isUsingSingleSlot()) { + Structure* transition = singleTransition(); + return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0; + } + return map()->get(make_pair(rep, attributes)); +} + +inline void StructureTransitionTable::add(JSGlobalData& globalData, Structure* structure) +{ + if (isUsingSingleSlot()) { + Structure* existingTransition = singleTransition(); + + // This handles the first transition being added. + if (!existingTransition) { + setSingleTransition(globalData, structure); + return; + } + + // This handles the second transition being added + // (or the first transition being despecified!) + setMap(new TransitionMap()); + add(globalData, existingTransition); + } + + // Add the structure to the map. + + // Newer versions of the STL have an std::make_pair function that takes rvalue references. + // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue. + // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details + std::pair<TransitionMap::iterator, bool> result = map()->add(globalData, make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure); + if (!result.second) { + // There already is an entry! - we should only hit this when despecifying. + ASSERT(result.first.get().second->m_specificValueInPrevious); + ASSERT(!structure->m_specificValueInPrevious); + map()->set(result.first, 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; + + switch (structure->m_transitionTable.size()) { + case 0: + ++numberLeaf; + if (!structure->m_previous) + ++numberSingletons; + break; + + case 1: + ++numberUsingSingleSlot; + break; + } + + if (structure->m_propertyTable) { + ++numberWithPropertyMaps; + totalPropertyMapsSize += structure->m_propertyTable->sizeInMemory(); + } + } + + 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(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo) + : JSCell(globalData, globalData.structureStructure.get()) + , m_typeInfo(typeInfo) + , m_globalObject(globalData, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) + , m_prototype(globalData, this, prototype) + , m_classInfo(classInfo) + , m_propertyStorageCapacity(typeInfo.isFinalObject() ? JSFinalObject_inlineStorageCapacity : JSNonFinalObject_inlineStorageCapacity) + , m_offset(noOffset) + , m_dictionaryKind(NoneDictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(false) + , m_hasNonEnumerableProperties(false) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(0) + , m_preventExtensions(false) + , m_didTransition(false) + , m_staticFunctionReified(false) +{ +} + +const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; + +Structure::Structure(JSGlobalData& globalData) + : JSCell(CreatingEarlyCell) + , m_typeInfo(CompoundType, OverridesVisitChildren) + , m_prototype(globalData, this, jsNull()) + , m_classInfo(&s_info) + , m_propertyStorageCapacity(0) + , m_offset(noOffset) + , m_dictionaryKind(NoneDictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(false) + , m_hasNonEnumerableProperties(false) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(0) + , m_preventExtensions(false) + , m_didTransition(false) + , m_staticFunctionReified(false) +{ +} + +Structure::Structure(JSGlobalData& globalData, const Structure* previous) + : JSCell(globalData, globalData.structureStructure.get()) + , m_typeInfo(previous->typeInfo()) + , m_prototype(globalData, this, previous->storedPrototype()) + , m_classInfo(previous->m_classInfo) + , m_propertyStorageCapacity(previous->m_propertyStorageCapacity) + , m_offset(noOffset) + , m_dictionaryKind(previous->m_dictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties) + , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount) + , m_preventExtensions(previous->m_preventExtensions) + , m_didTransition(true) + , m_staticFunctionReified(previous->m_staticFunctionReified) +{ + if (previous->m_globalObject) + m_globalObject.set(globalData, this, previous->m_globalObject.get()); +} + +void Structure::destroy(JSCell* cell) +{ + jsCast<Structure*>(cell)->Structure::~Structure(); +} + +void Structure::materializePropertyMap(JSGlobalData& globalData) +{ + ASSERT(structure()->classInfo() == &s_info); + 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->m_propertyTable->copy(globalData, 0, m_offset + 1); + break; + } + + structures.append(structure); + } + + if (!m_propertyTable) + createPropertyMap(m_offset + 1); + + for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) { + structure = structures[i]; + PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); + m_propertyTable->add(entry); + } +} + +void Structure::growPropertyStorageCapacity() +{ + if (isUsingInlineStorage()) + m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity; + else + m_propertyStorageCapacity *= 2; +} + +void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, const Identifier& propertyName) +{ + StringImpl* rep = propertyName.impl(); + + materializePropertyMapIfNecessary(globalData); + + ASSERT(isDictionary()); + ASSERT(m_propertyTable); + + PropertyMapEntry* entry = m_propertyTable->find(rep).first; + ASSERT(entry); + entry->specificValue.clear(); +} + +Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) +{ + ASSERT(!structure->isDictionary()); + ASSERT(structure->isObject()); + + if (Structure* existingTransition = structure->m_transitionTable.get(propertyName.impl(), attributes)) { + JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get(); + if (specificValueInPrevious && specificValueInPrevious != specificValue) + return 0; + ASSERT(existingTransition->m_offset != noOffset); + offset = existingTransition->m_offset; + return existingTransition; + } + + return 0; +} + +Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) +{ + // 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 (specificValue && structure->m_transitionTable.contains(propertyName.impl(), attributes)) + specificValue = 0; + + ASSERT(!structure->isDictionary()); + ASSERT(structure->isObject()); + ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); + + if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; + + if (structure->transitionCount() > s_maxTransitionLength) { + Structure* transition = toCacheableDictionaryTransition(globalData, structure); + ASSERT(structure != transition); + offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue); + if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) + transition->growPropertyStorageCapacity(); + return transition; + } + + Structure* transition = create(globalData, structure); + + transition->m_cachedPrototypeChain.setMayBeNull(globalData, transition, structure->m_cachedPrototypeChain.get()); + transition->m_previous.set(globalData, transition, structure); + transition->m_nameInPrevious = propertyName.impl(); + transition->m_attributesInPrevious = attributes; + transition->m_specificValueInPrevious.setMayBeNull(globalData, transition, specificValue); + + if (structure->m_propertyTable) { + if (structure->m_isPinnedPropertyTable) + transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1); + else + transition->m_propertyTable = structure->m_propertyTable.release(); + } else { + if (structure->m_previous) + transition->materializePropertyMap(globalData); + else + transition->createPropertyMap(); + } + + offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue); + if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) + transition->growPropertyStorageCapacity(); + + transition->m_offset = offset; + structure->m_transitionTable.add(globalData, transition); + return transition; +} + +Structure* Structure::removePropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, size_t& offset) +{ + ASSERT(!structure->isUncacheableDictionary()); + + Structure* transition = toUncacheableDictionaryTransition(globalData, structure); + + offset = transition->remove(propertyName); + + return transition; +} + +Structure* Structure::changePrototypeTransition(JSGlobalData& globalData, Structure* structure, JSValue prototype) +{ + Structure* transition = create(globalData, structure); + + transition->m_prototype.set(globalData, transition, prototype); + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(globalData); + transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); + transition->pin(); + + return transition; +} + +Structure* Structure::despecifyFunctionTransition(JSGlobalData& globalData, Structure* structure, const Identifier& replaceFunction) +{ + ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); + Structure* transition = create(globalData, structure); + + ++transition->m_specificFunctionThrashCount; + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(globalData); + transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); + transition->pin(); + + if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + transition->despecifyAllFunctions(globalData); + else { + bool removed = transition->despecifyFunction(globalData, replaceFunction); + ASSERT_UNUSED(removed, removed); + } + + return transition; +} + +Structure* Structure::getterSetterTransition(JSGlobalData& globalData, Structure* structure) +{ + Structure* transition = create(globalData, structure); + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(globalData); + transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); + transition->pin(); + + return transition; +} + +Structure* Structure::toDictionaryTransition(JSGlobalData& globalData, Structure* structure, DictionaryKind kind) +{ + ASSERT(!structure->isUncacheableDictionary()); + + Structure* transition = create(globalData, structure); + + structure->materializePropertyMapIfNecessary(globalData); + transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); + transition->m_dictionaryKind = kind; + transition->pin(); + + return transition; +} + +Structure* Structure::toCacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure) +{ + return toDictionaryTransition(globalData, structure, CachedDictionaryKind); +} + +Structure* Structure::toUncacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure) +{ + return toDictionaryTransition(globalData, structure, UncachedDictionaryKind); +} + +// In future we may want to cache this transition. +Structure* Structure::sealTransition(JSGlobalData& globalData, Structure* structure) +{ + Structure* transition = preventExtensionsTransition(globalData, structure); + + if (transition->m_propertyTable) { + PropertyTable::iterator end = transition->m_propertyTable->end(); + for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter) + iter->attributes |= DontDelete; + } + + return transition; +} + +// In future we may want to cache this transition. +Structure* Structure::freezeTransition(JSGlobalData& globalData, Structure* structure) +{ + Structure* transition = preventExtensionsTransition(globalData, structure); + + if (transition->m_propertyTable) { + PropertyTable::iterator end = transition->m_propertyTable->end(); + for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter) + iter->attributes |= (DontDelete | ReadOnly); + } + + return transition; +} + +// In future we may want to cache this transition. +Structure* Structure::preventExtensionsTransition(JSGlobalData& globalData, Structure* structure) +{ + Structure* transition = create(globalData, structure); + + // Don't set m_offset, as one can not transition to this. + + structure->materializePropertyMapIfNecessary(globalData); + transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); + transition->m_preventExtensions = true; + transition->pin(); + + return transition; +} + +// In future we may want to cache this property. +bool Structure::isSealed(JSGlobalData& globalData) +{ + if (isExtensible()) + return false; + + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return true; + + PropertyTable::iterator end = m_propertyTable->end(); + for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { + if ((iter->attributes & DontDelete) != DontDelete) + return false; + } + return true; +} + +// In future we may want to cache this property. +bool Structure::isFrozen(JSGlobalData& globalData) +{ + if (isExtensible()) + return false; + + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return true; + + PropertyTable::iterator end = m_propertyTable->end(); + for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { + if ((iter->attributes & (DontDelete | ReadOnly)) != (DontDelete | ReadOnly)) + return false; + } + return true; +} + +Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObject* object) +{ + ASSERT(isDictionary()); + if (isUncacheableDictionary()) { + ASSERT(m_propertyTable); + + size_t propertyCount = m_propertyTable->size(); + Vector<JSValue> values(propertyCount); + + unsigned i = 0; + PropertyTable::iterator end = m_propertyTable->end(); + for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) { + values[i] = object->getDirectOffset(iter->offset); + // Update property table to have the new property offsets + iter->offset = i; + } + + // Copy the original property values into their final locations + for (unsigned i = 0; i < propertyCount; i++) + object->putDirectOffset(globalData, i, values[i]); + + m_propertyTable->clearDeletedOffsets(); + } + + m_dictionaryKind = NoneDictionaryKind; + return this; +} + +size_t Structure::addPropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue) +{ + ASSERT(!m_enumerationCache); + + if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; + + materializePropertyMapIfNecessaryForPinning(globalData); + + pin(); + + size_t offset = putSpecificValue(globalData, propertyName, attributes, specificValue); + if (propertyStorageSize() > propertyStorageCapacity()) + growPropertyStorageCapacity(); + return offset; +} + +size_t Structure::removePropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName) +{ + ASSERT(isUncacheableDictionary()); + ASSERT(!m_enumerationCache); + + materializePropertyMapIfNecessaryForPinning(globalData); + + pin(); + size_t offset = remove(propertyName); + return offset; +} + +void Structure::pin() +{ + ASSERT(m_propertyTable); + m_isPinnedPropertyTable = true; + m_previous.clear(); + m_nameInPrevious.clear(); +} + +#if DUMP_PROPERTYMAP_STATS + +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 + +#if !DO_PROPERTYMAP_CONSTENCY_CHECK + +inline void Structure::checkConsistency() +{ +} + +#endif + +PassOwnPtr<PropertyTable> Structure::copyPropertyTable(JSGlobalData& globalData, Structure* owner) +{ + return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : 0); +} + +PassOwnPtr<PropertyTable> Structure::copyPropertyTableForPinning(JSGlobalData& globalData, Structure* owner) +{ + return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : new PropertyTable(m_offset == noOffset ? 0 : m_offset)); +} + +size_t Structure::get(JSGlobalData& globalData, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue) +{ + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return WTF::notFound; + + PropertyMapEntry* entry = m_propertyTable->find(propertyName).first; + if (!entry) + return WTF::notFound; + + attributes = entry->attributes; + specificValue = entry->specificValue.get(); + return entry->offset; +} + +bool Structure::despecifyFunction(JSGlobalData& globalData, const Identifier& propertyName) +{ + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return false; + + ASSERT(!propertyName.isNull()); + PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first; + if (!entry) + return false; + + ASSERT(entry->specificValue); + entry->specificValue.clear(); + return true; +} + +void Structure::despecifyAllFunctions(JSGlobalData& globalData) +{ + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return; + + PropertyTable::iterator end = m_propertyTable->end(); + for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) + iter->specificValue.clear(); +} + +size_t Structure::putSpecificValue(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue) +{ + ASSERT(!propertyName.isNull()); + ASSERT(get(globalData, propertyName) == notFound); + + checkConsistency(); + if (attributes & DontEnum) + m_hasNonEnumerableProperties = true; + + StringImpl* rep = propertyName.impl(); + + if (!m_propertyTable) + createPropertyMap(); + + unsigned newOffset; + + if (m_propertyTable->hasDeletedOffset()) + newOffset = m_propertyTable->getDeletedOffset(); + else + newOffset = m_propertyTable->size(); + + m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue)); + + checkConsistency(); + return newOffset; +} + +size_t Structure::remove(const Identifier& propertyName) +{ + ASSERT(!propertyName.isNull()); + + checkConsistency(); + + StringImpl* rep = propertyName.impl(); + + if (!m_propertyTable) + return notFound; + + PropertyTable::find_iterator position = m_propertyTable->find(rep); + if (!position.first) + return notFound; + + size_t offset = position.first->offset; + + m_propertyTable->remove(position); + m_propertyTable->addDeletedOffset(offset); + + checkConsistency(); + return offset; +} + +void Structure::createPropertyMap(unsigned capacity) +{ + ASSERT(!m_propertyTable); + + checkConsistency(); + m_propertyTable = adoptPtr(new PropertyTable(capacity)); + checkConsistency(); +} + +void Structure::getPropertyNamesFromStructure(JSGlobalData& globalData, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return; + + bool knownUnique = !propertyNames.size(); + + PropertyTable::iterator end = m_propertyTable->end(); + for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { + ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum)); + if (!(iter->attributes & DontEnum) || (mode == IncludeDontEnumProperties)) { + if (knownUnique) + propertyNames.addKnownUnique(iter->key); + else + propertyNames.add(iter->key); + } + } +} + +void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Structure* thisObject = jsCast<Structure*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + JSCell::visitChildren(thisObject, visitor); + if (thisObject->m_globalObject) + visitor.append(&thisObject->m_globalObject); + if (!thisObject->isObject()) + thisObject->m_cachedPrototypeChain.clear(); + else { + if (thisObject->m_prototype) + visitor.append(&thisObject->m_prototype); + if (thisObject->m_cachedPrototypeChain) + visitor.append(&thisObject->m_cachedPrototypeChain); + } + if (thisObject->m_previous) + visitor.append(&thisObject->m_previous); + if (thisObject->m_specificValueInPrevious) + visitor.append(&thisObject->m_specificValueInPrevious); + if (thisObject->m_enumerationCache) + visitor.append(&thisObject->m_enumerationCache); + if (thisObject->m_propertyTable) { + PropertyTable::iterator end = thisObject->m_propertyTable->end(); + for (PropertyTable::iterator ptr = thisObject->m_propertyTable->begin(); ptr != end; ++ptr) { + if (ptr->specificValue) + visitor.append(&ptr->specificValue); + } + } +} + +#if DO_PROPERTYMAP_CONSTENCY_CHECK + +void PropertyTable::checkConsistency() +{ + ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); + ASSERT(m_indexMask); + ASSERT(m_indexSize == m_indexMask + 1); + ASSERT(!(m_indexSize & m_indexMask)); + + ASSERT(m_keyCount <= m_indexSize / 2); + ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2); + ASSERT(m_deletedCount <= m_indexSize / 4); + + unsigned indexCount = 0; + unsigned deletedIndexCount = 0; + for (unsigned a = 0; a != m_indexSize; ++a) { + unsigned entryIndex = m_index[a]; + if (entryIndex == PropertyTable::EmptyEntryIndex) + continue; + if (entryIndex == deletedEntryIndex()) { + ++deletedIndexCount; + continue; + } + ASSERT(entryIndex < deletedEntryIndex()); + ASSERT(entryIndex - 1 <= usedCount()); + ++indexCount; + + for (unsigned b = a + 1; b != m_indexSize; ++b) + ASSERT(m_index[b] != entryIndex); + } + ASSERT(indexCount == m_keyCount); + ASSERT(deletedIndexCount == m_deletedCount); + + ASSERT(!table()[deletedEntryIndex() - 1].key); + + unsigned nonEmptyEntryCount = 0; + for (unsigned c = 0; c < usedCount(); ++c) { + StringImpl* rep = table()[c].key; + if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) + continue; + ++nonEmptyEntryCount; + unsigned i = rep->existingHash(); + unsigned k = 0; + unsigned entryIndex; + while (1) { + entryIndex = m_index[i & m_indexMask]; + ASSERT(entryIndex != PropertyTable::EmptyEntryIndex); + if (rep == table()[entryIndex - 1].key) + break; + if (k == 0) + k = 1 | doubleHash(rep->existingHash()); + i += k; + } + ASSERT(entryIndex == c + 1); + } + + ASSERT(nonEmptyEntryCount == m_keyCount); +} + +void Structure::checkConsistency() +{ + if (!m_propertyTable) + return; + + if (!m_hasNonEnumerableProperties) { + PropertyTable::iterator end = m_propertyTable->end(); + for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { + ASSERT(!(iter->attributes & DontEnum)); + } + } + + m_propertyTable->checkConsistency(); +} + +#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 000000000..70e968014 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -0,0 +1,406 @@ +/* + * 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 "ClassInfo.h" +#include "Identifier.h" +#include "JSCell.h" +#include "JSType.h" +#include "JSValue.h" +#include "PropertyMapHashTable.h" +#include "PropertyNameArray.h" +#include "Protect.h" +#include "StructureTransitionTable.h" +#include "JSTypeInfo.h" +#include "UString.h" +#include "Weak.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + + +namespace JSC { + + class PropertyNameArray; + class PropertyNameArrayData; + class StructureChain; + class SlotVisitor; + + class Structure : public JSCell { + public: + friend class StructureTransitionTable; + + typedef JSCell Base; + + static Structure* create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo) + { + ASSERT(globalData.structureStructure); + ASSERT(classInfo); + Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo); + structure->finishCreation(globalData); + return structure; + } + + protected: + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + ASSERT(m_prototype); + ASSERT(m_prototype.isObject() || m_prototype.isNull()); + } + + void finishCreation(JSGlobalData& globalData, CreatingEarlyCellTag) + { + Base::finishCreation(globalData, this, CreatingEarlyCell); + ASSERT(m_prototype); + ASSERT(m_prototype.isNull()); + ASSERT(!globalData.structureStructure); + } + + public: + static void dumpStatistics(); + + static Structure* addPropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); + static Structure* addPropertyTransitionToExistingStructure(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); + static Structure* removePropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, size_t& offset); + static Structure* changePrototypeTransition(JSGlobalData&, Structure*, JSValue prototype); + static Structure* despecifyFunctionTransition(JSGlobalData&, Structure*, const Identifier&); + static Structure* getterSetterTransition(JSGlobalData&, Structure*); + static Structure* toCacheableDictionaryTransition(JSGlobalData&, Structure*); + static Structure* toUncacheableDictionaryTransition(JSGlobalData&, Structure*); + static Structure* sealTransition(JSGlobalData&, Structure*); + static Structure* freezeTransition(JSGlobalData&, Structure*); + static Structure* preventExtensionsTransition(JSGlobalData&, Structure*); + + bool isSealed(JSGlobalData&); + bool isFrozen(JSGlobalData&); + bool isExtensible() const { return !m_preventExtensions; } + bool didTransition() const { return m_didTransition; } + + Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*); + + static void destroy(JSCell*); + + // These should be used with caution. + size_t addPropertyWithoutTransition(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue); + size_t removePropertyWithoutTransition(JSGlobalData&, const Identifier& propertyName); + void setPrototypeWithoutTransition(JSGlobalData& globalData, JSValue prototype) { m_prototype.set(globalData, this, prototype); } + + bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; } + bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; } + + // Type accessors. + const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == &s_info); return m_typeInfo; } + bool isObject() const { return typeInfo().isObject(); } + + + JSGlobalObject* globalObject() const { return m_globalObject.get(); } + void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); } + + JSValue storedPrototype() const { return m_prototype.get(); } + JSValue prototypeForLookup(ExecState*) const; + StructureChain* prototypeChain(ExecState*) const; + static void visitChildren(JSCell*, SlotVisitor&); + + Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); } + bool transitivelyTransitionedFrom(Structure* structureToFind); + + void growPropertyStorageCapacity(); + unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; } + unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); } + bool isUsingInlineStorage() const; + + size_t get(JSGlobalData&, const Identifier& propertyName); + size_t get(JSGlobalData&, const UString& name); + size_t get(JSGlobalData&, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue); + size_t get(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue) + { + ASSERT(!propertyName.isNull()); + ASSERT(structure()->classInfo() == &s_info); + return get(globalData, propertyName.impl(), attributes, specificValue); + } + + bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } + void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; } + + bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } + + bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; } + + void despecifyDictionaryFunction(JSGlobalData&, const Identifier& propertyName); + void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } + + void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. + JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. + void getPropertyNamesFromStructure(JSGlobalData&, PropertyNameArray&, EnumerationMode); + + bool staticFunctionsReified() + { + return m_staticFunctionReified; + } + + void setStaticFunctionsReified() + { + m_staticFunctionReified = true; + } + + const ClassInfo* classInfo() const { return m_classInfo; } + + static ptrdiff_t prototypeOffset() + { + return OBJECT_OFFSETOF(Structure, m_prototype); + } + + static ptrdiff_t typeInfoFlagsOffset() + { + return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset(); + } + + static ptrdiff_t typeInfoTypeOffset() + { + return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset(); + } + + static Structure* createStructure(JSGlobalData& globalData) + { + ASSERT(!globalData.structureStructure); + Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData); + structure->finishCreation(globalData, CreatingEarlyCell); + return structure; + } + + static JS_EXPORTDATA const ClassInfo s_info; + + private: + Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*); + Structure(JSGlobalData&); + Structure(JSGlobalData&, const Structure*); + + static Structure* create(JSGlobalData& globalData, const Structure* structure) + { + ASSERT(globalData.structureStructure); + Structure* newStructure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, structure); + newStructure->finishCreation(globalData); + return newStructure; + } + + typedef enum { + NoneDictionaryKind = 0, + CachedDictionaryKind = 1, + UncachedDictionaryKind = 2 + } DictionaryKind; + static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind); + + size_t putSpecificValue(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue); + size_t remove(const Identifier& propertyName); + + void createPropertyMap(unsigned keyCount = 0); + void checkConsistency(); + + bool despecifyFunction(JSGlobalData&, const Identifier&); + void despecifyAllFunctions(JSGlobalData&); + + PassOwnPtr<PropertyTable> copyPropertyTable(JSGlobalData&, Structure* owner); + PassOwnPtr<PropertyTable> copyPropertyTableForPinning(JSGlobalData&, Structure* owner); + void materializePropertyMap(JSGlobalData&); + void materializePropertyMapIfNecessary(JSGlobalData& globalData) + { + ASSERT(structure()->classInfo() == &s_info); + if (!m_propertyTable && m_previous) + materializePropertyMap(globalData); + } + void materializePropertyMapIfNecessaryForPinning(JSGlobalData& globalData) + { + ASSERT(structure()->classInfo() == &s_info); + if (!m_propertyTable) + materializePropertyMap(globalData); + } + + int 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; + } + + bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; + + void pin(); + + static const int s_maxTransitionLength = 64; + + static const int noOffset = -1; + + static const unsigned maxSpecificFunctionThrashCount = 3; + + TypeInfo m_typeInfo; + + WriteBarrier<JSGlobalObject> m_globalObject; + WriteBarrier<Unknown> m_prototype; + mutable WriteBarrier<StructureChain> m_cachedPrototypeChain; + + WriteBarrier<Structure> m_previous; + RefPtr<StringImpl> m_nameInPrevious; + WriteBarrier<JSCell> m_specificValueInPrevious; + + const ClassInfo* m_classInfo; + + StructureTransitionTable m_transitionTable; + + WriteBarrier<JSPropertyNameIterator> m_enumerationCache; + + OwnPtr<PropertyTable> m_propertyTable; + + uint32_t m_propertyStorageCapacity; + + // m_offset does not account for anonymous slots + int m_offset; + + unsigned m_dictionaryKind : 2; + bool m_isPinnedPropertyTable : 1; + bool m_hasGetterSetterProperties : 1; + bool m_hasNonEnumerableProperties : 1; + unsigned m_attributesInPrevious : 7; + unsigned m_specificFunctionThrashCount : 2; + unsigned m_preventExtensions : 1; + unsigned m_didTransition : 1; + unsigned m_staticFunctionReified; + }; + + inline size_t Structure::get(JSGlobalData& globalData, const Identifier& propertyName) + { + ASSERT(structure()->classInfo() == &s_info); + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return notFound; + + PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first; + return entry ? entry->offset : notFound; + } + + inline size_t Structure::get(JSGlobalData& globalData, const UString& name) + { + ASSERT(structure()->classInfo() == &s_info); + materializePropertyMapIfNecessary(globalData); + if (!m_propertyTable) + return notFound; + + PropertyMapEntry* entry = m_propertyTable->findWithString(name.impl()).first; + return entry ? entry->offset : notFound; + } + + inline bool JSCell::isObject() const + { + return m_structure->isObject(); + } + + inline bool JSCell::isString() const + { + return m_structure->typeInfo().type() == StringType; + } + + inline bool JSCell::isGetterSetter() const + { + return m_structure->typeInfo().type() == GetterSetterType; + } + + inline bool JSCell::isAPIValueWrapper() const + { + return m_structure->typeInfo().type() == APIValueWrapperType; + } + + inline void JSCell::setStructure(JSGlobalData& globalData, Structure* structure) + { + ASSERT(structure->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren()); + ASSERT(structure->classInfo() == m_structure->classInfo()); + m_structure.set(globalData, this, structure); + } + + inline const ClassInfo* JSCell::validatedClassInfo() const + { +#if ENABLE(GC_VALIDATION) + ASSERT(m_structure.unvalidatedGet()->classInfo() == m_classInfo); +#else + ASSERT(m_structure->classInfo() == m_classInfo); +#endif + return m_classInfo; + } + + ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell) + { + ASSERT(!m_isCheckingForDefaultMarkViolation); +#if ENABLE(GC_VALIDATION) + validate(cell); +#endif + m_visitCount++; + if (Heap::testAndSetMarked(cell) || !cell->structure()) + return; + + // Should never attempt to mark something that is zapped. + ASSERT(!cell->isZapped()); + + m_stack.append(cell); + } + + inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure) + { + // Newer versions of the STL have an std::make_pair function that takes rvalue references. + // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue. + // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details. + return Hash::Key(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious); + } + + inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind) + { + for (Structure* current = this; current; current = current->previousID()) { + if (current == structureToFind) + return true; + } + return false; + } + + inline JSCell::JSCell(JSGlobalData& globalData, Structure* structure) + : m_classInfo(structure->classInfo()) + , m_structure(globalData, this, structure) + { + } + + inline void JSCell::finishCreation(JSGlobalData& globalData, Structure* structure, CreatingEarlyCellTag) + { +#if ENABLE(GC_VALIDATION) + ASSERT(globalData.isInitializingObject()); + globalData.setInitializingObject(false); + if (structure) +#endif + m_structure.setEarlyValue(globalData, this, structure); + m_classInfo = structure->classInfo(); + // Very first set of allocations won't have a real structure. + ASSERT(m_structure || !globalData.structureStructure); + } + +} // namespace JSC + +#endif // Structure_h diff --git a/Source/JavaScriptCore/runtime/StructureChain.cpp b/Source/JavaScriptCore/runtime/StructureChain.cpp new file mode 100644 index 000000000..afb2d9501 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureChain.cpp @@ -0,0 +1,57 @@ +/* + * 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 { + +ClassInfo StructureChain::s_info = { "StructureChain", 0, 0, 0, CREATE_METHOD_TABLE(StructureChain) }; + +StructureChain::StructureChain(JSGlobalData& globalData, Structure* structure) + : JSCell(globalData, structure) +{ +} + +void StructureChain::destroy(JSCell* cell) +{ + jsCast<StructureChain*>(cell)->StructureChain::~StructureChain(); +} + +void StructureChain::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + StructureChain* thisObject = jsCast<StructureChain*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + size_t i = 0; + while (thisObject->m_vector[i]) + visitor.append(&thisObject->m_vector[i++]); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StructureChain.h b/Source/JavaScriptCore/runtime/StructureChain.h new file mode 100644 index 000000000..df7a37fa7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureChain.h @@ -0,0 +1,84 @@ +/* + * 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 "JSCell.h" +#include "JSObject.h" +#include "Structure.h" + +#include <wtf/OwnArrayPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace JSC { + + class Structure; + + class StructureChain : public JSCell { + friend class JIT; + + public: + typedef JSCell Base; + + static StructureChain* create(JSGlobalData& globalData, Structure* head) + { + StructureChain* chain = new (NotNull, allocateCell<StructureChain>(globalData.heap)) StructureChain(globalData, globalData.structureChainStructure.get()); + chain->finishCreation(globalData, head); + return chain; + } + WriteBarrier<Structure>* head() { return m_vector.get(); } + static void visitChildren(JSCell*, SlotVisitor&); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) { return Structure::create(globalData, globalObject, prototype, TypeInfo(CompoundType, OverridesVisitChildren), &s_info); } + + static ClassInfo s_info; + + protected: + void finishCreation(JSGlobalData& globalData, Structure* head) + { + Base::finishCreation(globalData); + size_t size = 0; + for (Structure* current = head; current; current = current->storedPrototype().isNull() ? 0 : asObject(current->storedPrototype())->structure()) + ++size; + + m_vector = adoptArrayPtr(new WriteBarrier<Structure>[size + 1]); + + size_t i = 0; + for (Structure* current = head; current; current = current->storedPrototype().isNull() ? 0 : asObject(current->storedPrototype())->structure()) + m_vector[i++].set(globalData, this, current); + } + + private: + StructureChain(JSGlobalData&, Structure*); + static void destroy(JSCell*); + OwnArrayPtr<WriteBarrier<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 000000000..536237a33 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -0,0 +1,172 @@ +/* + * 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 "WeakGCMap.h" +#include <wtf/HashFunctions.h> +#include <wtf/HashTraits.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefPtr.h> + +namespace JSC { + +class Structure; + +class StructureTransitionTable { + static const intptr_t UsingSingleSlotFlag = 1; + + struct Hash { + 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 HashTraits { + 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 const int minimumTableSize = FirstTraits::minimumTableSize; + + static void constructDeletedValue(TraitType& slot) { FirstTraits::constructDeletedValue(slot.first); } + static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } + }; + + struct WeakGCMapFinalizerCallback { + static void* finalizerContextFor(Hash::Key) + { + return 0; + } + + static inline Hash::Key keyForFinalizer(void* context, Structure* structure) + { + return keyForWeakGCMapFinalizer(context, structure); + } + }; + + typedef WeakGCMap<Hash::Key, Structure, WeakGCMapFinalizerCallback, Hash, HashTraits> TransitionMap; + + static Hash::Key keyForWeakGCMapFinalizer(void* context, Structure*); + +public: + StructureTransitionTable() + : m_data(UsingSingleSlotFlag) + { + } + + ~StructureTransitionTable() + { + if (!isUsingSingleSlot()) { + delete map(); + return; + } + + HandleSlot slot = this->slot(); + if (!slot) + return; + HandleHeap::heapFor(slot)->deallocate(slot); + } + + inline void add(JSGlobalData&, Structure*); + inline bool contains(StringImpl* rep, unsigned attributes) const; + inline Structure* get(StringImpl* rep, unsigned attributes) const; + +private: + bool isUsingSingleSlot() const + { + return m_data & UsingSingleSlotFlag; + } + + TransitionMap* map() const + { + ASSERT(!isUsingSingleSlot()); + return reinterpret_cast<TransitionMap*>(m_data); + } + + HandleSlot slot() const + { + ASSERT(isUsingSingleSlot()); + return reinterpret_cast<HandleSlot>(m_data & ~UsingSingleSlotFlag); + } + + void setMap(TransitionMap* map) + { + ASSERT(isUsingSingleSlot()); + + if (HandleSlot slot = this->slot()) + HandleHeap::heapFor(slot)->deallocate(slot); + + // This implicitly clears the flag that indicates we're using a single transition + m_data = reinterpret_cast<intptr_t>(map); + + ASSERT(!isUsingSingleSlot()); + } + + Structure* singleTransition() const + { + ASSERT(isUsingSingleSlot()); + if (HandleSlot slot = this->slot()) { + if (*slot) + return reinterpret_cast<Structure*>(slot->asCell()); + } + return 0; + } + + void setSingleTransition(JSGlobalData& globalData, Structure* structure) + { + ASSERT(isUsingSingleSlot()); + HandleSlot slot = this->slot(); + if (!slot) { + slot = globalData.heap.handleHeap()->allocate(); + HandleHeap::heapFor(slot)->makeWeak(slot, 0, 0); + m_data = reinterpret_cast<intptr_t>(slot) | UsingSingleSlotFlag; + } + HandleHeap::heapFor(slot)->writeBarrier(slot, reinterpret_cast<JSCell*>(structure)); + *slot = reinterpret_cast<JSCell*>(structure); + } + + intptr_t m_data; +}; + +} // namespace JSC + +#endif // StructureTransitionTable_h diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h new file mode 100644 index 000000000..81731222b --- /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> +#include <wtf/HashTraits.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 : HashTraits<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> { + WTF_MAKE_FAST_ALLOCATED; + public: + static PassRefPtr<SharedSymbolTable> create() { return adoptRef(new SharedSymbolTable); } + private: + SharedSymbolTable() { deprecatedTurnOffVerifier(); } + }; + +} // namespace JSC + +#endif // SymbolTable_h diff --git a/Source/JavaScriptCore/runtime/Terminator.h b/Source/JavaScriptCore/runtime/Terminator.h new file mode 100644 index 000000000..6b0f2366f --- /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 000000000..3065c99ed --- /dev/null +++ b/Source/JavaScriptCore/runtime/TimeoutChecker.cpp @@ -0,0 +1,142 @@ +/* + * 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 + +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; +#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 static_cast<unsigned> ((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()->globalObjectMethodTable()->shouldInterruptScript(exec->dynamicGlobalObject())) + 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 000000000..5925641f8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TimeoutChecker.h @@ -0,0 +1,74 @@ +/* + * 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> + +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(); + ++m_startCount; + } + + void stop() + { + ASSERT(m_startCount); + --m_startCount; + } + + 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 000000000..da854b990 --- /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 000000000..c28c85f61 --- /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 000000000..5b1e9a0e0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/UString.cpp @@ -0,0 +1,475 @@ +/* + * 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 "Heap.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/MathExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/Vector.h> +#include <wtf/dtoa.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 { + +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 LChar* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) +{ +} + +UString::UString(const char* characters, unsigned length) + : m_impl(characters ? StringImpl::create(reinterpret_cast<const LChar*>(characters), length) : 0) +{ +} + +// Construct a string with latin1 data, from a null-terminated source. +UString::UString(const LChar* characters) + : m_impl(characters ? StringImpl::create(characters) : 0) +{ +} + +UString::UString(const char* characters) + : m_impl(characters ? StringImpl::create(reinterpret_cast<const LChar*>(characters)) : 0) +{ +} + +UString UString::number(int i) +{ + LChar buf[1 + sizeof(i) * 3]; + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* 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) +{ + LChar buf[1 + sizeof(i) * 3]; + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* 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) +{ + LChar buf[sizeof(u) * 3]; + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* 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) +{ + LChar buf[1 + sizeof(l) * 3]; + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* 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; + return UString(numberToString(d, buffer)); +} + +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 (s1.isEmpty()) + return !s2; + + return equal(s1.impl(), s2); +} + +// This method assumes that all simple checks have been performed by +// the inlined operator==() in the header file. +bool equalSlowCase(const UString& s1, const UString& s2) +{ + StringImpl* rep1 = s1.impl(); + StringImpl* rep2 = s2.impl(); + unsigned size1 = rep1->length(); + + // At this point we know + // (a) that the strings are the same length and + // (b) that they are greater than zero length. + bool s1Is8Bit = rep1->is8Bit(); + bool s2Is8Bit = rep2->is8Bit(); + + if (s1Is8Bit) { + const LChar* d1 = rep1->characters8(); + if (s2Is8Bit) { + const LChar* d2 = rep2->characters8(); + + 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(LChar))); + } + } + + const UChar* d2 = rep2->characters16(); + + for (unsigned i = 0; i < size1; i++) { + if (d1[i] != d2[i]) + return false; + } + return true; + } + + if (s2Is8Bit) { + const UChar* d1 = rep1->characters16(); + const LChar* d2 = rep2->characters8(); + + for (unsigned i = 0; i < size1; i++) { + if (d1[i] != d2[i]) + return false; + } + return true; + + } + + const UChar* d1 = rep1->characters16(); + const UChar* d2 = rep2->characters16(); + + 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))); + } +} + +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; + if (s1.is8Bit() && s2.is8Bit()) { + const LChar* c1 = s1.characters8(); + const LChar* c2 = s2.characters8(); + unsigned length = 0; + while (length < lmin && *c1 == *c2) { + c1++; + c2++; + length++; + } + if (length < lmin) + return (c1[0] < c2[0]); + + return (l1 < l2); + } + const UChar* c1 = s1.characters(); + const UChar* c2 = s2.characters(); + unsigned length = 0; + while (length < lmin && *c1 == *c2) { + c1++; + c2++; + length++; + } + if (length < 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(); + + if (this->is8Bit()) { + const LChar* characters = this->characters8(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + LChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; + } + + return result; + } + + const UChar* characters = this->characters16(); + + 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(); + + if (!length) + return CString("", 0); + + // 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(); + + if (is8Bit()) { + const LChar* characters = this->characters8(); + + ConversionResult result = convertLatin1ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size()); + ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be sufficient for any conversion + } else { + const UChar* characters = this->characters16(); + + 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 000000000..c05ae5081 --- /dev/null +++ b/Source/JavaScriptCore/runtime/UString.h @@ -0,0 +1,288 @@ +/* + * 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 LChar* characters, unsigned length); + UString(const char* characters, unsigned length); + + // Construct a string with latin1 data, from a null-terminated source. + UString(const LChar* characters); + 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<typename CharType, size_t inlineCapacity> + static UString adopt(Vector<CharType, 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(); + } + + const LChar* characters8() const + { + if (!m_impl) + return 0; + ASSERT(m_impl->is8Bit()); + return m_impl->characters8(); + } + + const UChar* characters16() const + { + if (!m_impl) + return 0; + ASSERT(!m_impl->is8Bit()); + return m_impl->characters16(); + } + + template <typename CharType> + inline const CharType* getCharacters() const; + + bool is8Bit() const { return m_impl->is8Bit(); } + + 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; + if (is8Bit()) + return m_impl->characters8()[index]; + return m_impl->characters16()[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 LChar* 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; +}; + +template<> +inline const LChar* UString::getCharacters<LChar>() const +{ + ASSERT(is8Bit()); + return characters8(); +} + +template<> +inline const UChar* UString::getCharacters<UChar>() const +{ + ASSERT(!is8Bit()); + return characters16(); +} + +NEVER_INLINE bool equalSlowCase(const UString& s1, const UString& s2); + +ALWAYS_INLINE bool operator==(const UString& s1, const UString& s2) +{ + StringImpl* rep1 = s1.impl(); + StringImpl* rep2 = s2.impl(); + + if (rep1 == rep2) // If they're the same rep, they're equal. + return true; + + unsigned size1 = 0; + unsigned size2 = 0; + + 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; + + if (size1 == 1) + return (*rep1)[0u] == (*rep2)[0u]; + + return equalSlowCase(s1, s2); +} + + +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) || CPU(SPARC) + 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 { }; + +} // namespace WTF + +#endif + diff --git a/Source/JavaScriptCore/runtime/UStringBuilder.h b/Source/JavaScriptCore/runtime/UStringBuilder.h new file mode 100644 index 000000000..31ccf382a --- /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 000000000..cbd4e60ca --- /dev/null +++ b/Source/JavaScriptCore/runtime/UStringConcatenate.h @@ -0,0 +1,141 @@ +/* + * 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_string(string) + , m_length(string.length()) + { + } + + unsigned length() { return m_length; } + + bool is8Bit() { return m_string.isNull() || m_string.is8Bit(); } + + void writeTo(LChar* destination) + { + const LChar* characters = m_string.characters8(); + for (unsigned i = 0; i < m_length; ++i) + destination[i] = characters[i]; + } + + void writeTo(UChar* destination) + { + if (is8Bit()) { + const LChar* characters = m_string.characters8(); + for (unsigned i = 0; i < m_length; ++i) + destination[i] = characters[i]; + } else { + const UChar* characters = m_string.characters16(); + for (unsigned i = 0; i < m_length; ++i) + destination[i] = characters[i]; + } + } + +private: + const JSC::UString& m_string; + 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/Uint16WithFraction.h b/Source/JavaScriptCore/runtime/Uint16WithFraction.h new file mode 100644 index 000000000..0e5c5f91c --- /dev/null +++ b/Source/JavaScriptCore/runtime/Uint16WithFraction.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2011 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 Uint16WithFraction_h +#define Uint16WithFraction_h + +#include <wtf/MathExtras.h> + +namespace JSC { + +// Would be nice if this was a static const member, but the OS X linker +// seems to want a symbol in the binary in that case... +#define oneGreaterThanMaxUInt16 0x10000 + +// A uint16_t with an infinite precision fraction. Upon overflowing +// the uint16_t range, this class will clamp to oneGreaterThanMaxUInt16. +// This is used in converting the fraction part of a number to a string. +class Uint16WithFraction { +public: + explicit Uint16WithFraction(double number, uint16_t divideByExponent = 0) + { + ASSERT(number && isfinite(number) && !signbit(number)); + + // Check for values out of uint16_t range. + if (number >= oneGreaterThanMaxUInt16) { + m_values.append(oneGreaterThanMaxUInt16); + m_leadingZeros = 0; + return; + } + + // Append the units to m_values. + double integerPart = floor(number); + m_values.append(static_cast<uint32_t>(integerPart)); + + bool sign; + int32_t exponent; + uint64_t mantissa; + decomposeDouble(number - integerPart, sign, exponent, mantissa); + ASSERT(!sign && exponent < 0); + exponent -= divideByExponent; + + int32_t zeroBits = -exponent; + --zeroBits; + + // Append the append words for to m_values. + while (zeroBits >= 32) { + m_values.append(0); + zeroBits -= 32; + } + + // Left align the 53 bits of the mantissa within 96 bits. + uint32_t values[3]; + values[0] = static_cast<uint32_t>(mantissa >> 21); + values[1] = static_cast<uint32_t>(mantissa << 11); + values[2] = 0; + // Shift based on the remainder of the exponent. + if (zeroBits) { + values[2] = values[1] << (32 - zeroBits); + values[1] = (values[1] >> zeroBits) | (values[0] << (32 - zeroBits)); + values[0] = (values[0] >> zeroBits); + } + m_values.append(values[0]); + m_values.append(values[1]); + m_values.append(values[2]); + + // Canonicalize; remove any trailing zeros. + while (m_values.size() > 1 && !m_values.last()) + m_values.removeLast(); + + // Count the number of leading zero, this is useful in optimizing multiplies. + m_leadingZeros = 0; + while (m_leadingZeros < m_values.size() && !m_values[m_leadingZeros]) + ++m_leadingZeros; + } + + Uint16WithFraction& operator*=(uint16_t multiplier) + { + ASSERT(checkConsistency()); + + // iteratate backwards over the fraction until we reach the leading zeros, + // passing the carry from one calculation into the next. + uint64_t accumulator = 0; + for (size_t i = m_values.size(); i > m_leadingZeros; ) { + --i; + accumulator += static_cast<uint64_t>(m_values[i]) * static_cast<uint64_t>(multiplier); + m_values[i] = static_cast<uint32_t>(accumulator); + accumulator >>= 32; + } + + if (!m_leadingZeros) { + // With a multiplicand and multiplier in the uint16_t range, this cannot carry + // (even allowing for the infinity value). + ASSERT(!accumulator); + // Check for overflow & clamp to 'infinity'. + if (m_values[0] >= oneGreaterThanMaxUInt16) { + m_values.shrink(1); + m_values[0] = oneGreaterThanMaxUInt16; + m_leadingZeros = 0; + return *this; + } + } else if (accumulator) { + // Check for carry from the last multiply, if so overwrite last leading zero. + m_values[--m_leadingZeros] = static_cast<uint32_t>(accumulator); + // The limited range of the multiplier should mean that even if we carry into + // the units, we don't need to check for overflow of the uint16_t range. + ASSERT(m_values[0] < oneGreaterThanMaxUInt16); + } + + // Multiplication by an even value may introduce trailing zeros; if so, clean them + // up. (Keeping the value in a normalized form makes some of the comparison operations + // more efficient). + while (m_values.size() > 1 && !m_values.last()) + m_values.removeLast(); + ASSERT(checkConsistency()); + return *this; + } + + bool operator<(const Uint16WithFraction& other) + { + ASSERT(checkConsistency()); + ASSERT(other.checkConsistency()); + + // Iterate over the common lengths of arrays. + size_t minSize = std::min(m_values.size(), other.m_values.size()); + for (size_t index = 0; index < minSize; ++index) { + // If we find a value that is not equal, compare and return. + uint32_t fromThis = m_values[index]; + uint32_t fromOther = other.m_values[index]; + if (fromThis != fromOther) + return fromThis < fromOther; + } + // If these numbers have the same lengths, they are equal, + // otherwise which ever number has a longer fraction in larger. + return other.m_values.size() > minSize; + } + + // Return the floor (non-fractional portion) of the number, clearing this to zero, + // leaving the fractional part unchanged. + uint32_t floorAndSubtract() + { + // 'floor' is simple the integer portion of the value. + uint32_t floor = m_values[0]; + + // If floor is non-zero, + if (floor) { + m_values[0] = 0; + m_leadingZeros = 1; + while (m_leadingZeros < m_values.size() && !m_values[m_leadingZeros]) + ++m_leadingZeros; + } + + return floor; + } + + // Compare this value to 0.5, returns -1 for less than, 0 for equal, 1 for greater. + int comparePoint5() + { + ASSERT(checkConsistency()); + // If units != 0, this is greater than 0.5. + if (m_values[0]) + return 1; + // If size == 1 this value is 0, hence < 0.5. + if (m_values.size() == 1) + return -1; + // Compare to 0.5. + if (m_values[1] > 0x80000000ul) + return 1; + if (m_values[1] < 0x80000000ul) + return -1; + // Check for more words - since normalized numbers have no trailing zeros, if + // there are more that two digits we can assume at least one more is non-zero, + // and hence the value is > 0.5. + return m_values.size() > 2 ? 1 : 0; + } + + // Return true if the sum of this plus addend would be greater than 1. + bool sumGreaterThanOne(const Uint16WithFraction& addend) + { + ASSERT(checkConsistency()); + ASSERT(addend.checkConsistency()); + + // First, sum the units. If the result is greater than one, return true. + // If equal to one, return true if either number has a fractional part. + uint32_t sum = m_values[0] + addend.m_values[0]; + if (sum) + return sum > 1 || std::max(m_values.size(), addend.m_values.size()) > 1; + + // We could still produce a result greater than zero if addition of the next + // word from the fraction were to carry, leaving a result > 0. + + // Iterate over the common lengths of arrays. + size_t minSize = std::min(m_values.size(), addend.m_values.size()); + for (size_t index = 1; index < minSize; ++index) { + // Sum the next word from this & the addend. + uint32_t fromThis = m_values[index]; + uint32_t fromAddend = addend.m_values[index]; + sum = fromThis + fromAddend; + + // Check for overflow. If so, check whether the remaining result is non-zero, + // or if there are any further words in the fraction. + if (sum < fromThis) + return sum || (index + 1) < std::max(m_values.size(), addend.m_values.size()); + + // If the sum is uint32_t max, then we would carry a 1 if addition of the next + // digits in the number were to overflow. + if (sum != 0xFFFFFFFF) + return false; + } + return false; + } + +private: + bool checkConsistency() const + { + // All values should have at least one value. + return (m_values.size()) + // The units value must be a uint16_t, or the value is the overflow value. + && (m_values[0] < oneGreaterThanMaxUInt16 || (m_values[0] == oneGreaterThanMaxUInt16 && m_values.size() == 1)) + // There should be no trailing zeros (unless this value is zero!). + && (m_values.last() || m_values.size() == 1); + } + + // The internal storage of the number. This vector is always at least one entry in size, + // with the first entry holding the portion of the number greater than zero. The first + // value always hold a value in the uint16_t range, or holds the value oneGreaterThanMaxUInt16 to + // indicate the value has overflowed to >= 0x10000. If the units value is oneGreaterThanMaxUInt16, + // there can be no fraction (size must be 1). + // + // Subsequent values in the array represent portions of the fractional part of this number. + // The total value of the number is the sum of (m_values[i] / pow(2^32, i)), for each i + // in the array. The vector should contain no trailing zeros, except for the value '0', + // represented by a vector contianing a single zero value. These constraints are checked + // by 'checkConsistency()', above. + // + // The inline capacity of the vector is set to be able to contain any IEEE double (1 for + // the units column, 32 for zeros introduced due to an exponent up to -3FE, and 2 for + // bits taken from the mantissa). + Vector<uint32_t, 36> m_values; + + // Cache a count of the number of leading zeros in m_values. We can use this to optimize + // methods that would otherwise need visit all words in the vector, e.g. multiplication. + size_t m_leadingZeros; +}; + +} + +#endif + diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h new file mode 100644 index 000000000..1bb3cd5bb --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakGCMap.h @@ -0,0 +1,191 @@ +/* + * 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 "Handle.h" +#include "JSGlobalData.h" +#include <wtf/HashMap.h> + +namespace JSC { + +// A HashMap for GC'd values that removes entries when the associated value +// dies. +template <typename KeyType, typename MappedType> struct DefaultWeakGCMapFinalizerCallback { + static void* finalizerContextFor(KeyType key) + { + return reinterpret_cast<void*>(key); + } + + static KeyType keyForFinalizer(void* context, typename HandleTypes<MappedType>::ExternalType) + { + return reinterpret_cast<KeyType>(context); + } +}; + +template<typename KeyType, typename MappedType, typename FinalizerCallback = DefaultWeakGCMapFinalizerCallback<KeyType, MappedType>, typename HashArg = typename DefaultHash<KeyType>::Hash, typename KeyTraitsArg = HashTraits<KeyType> > +class WeakGCMap : private WeakHandleOwner { + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(WeakGCMap); + + typedef HashMap<KeyType, HandleSlot, HashArg, KeyTraitsArg> MapType; + typedef typename HandleTypes<MappedType>::ExternalType ExternalType; + typedef typename MapType::iterator map_iterator; + +public: + + struct iterator { + friend class WeakGCMap; + iterator(map_iterator iter) + : m_iterator(iter) + { + } + + std::pair<KeyType, ExternalType> get() const { return std::make_pair(m_iterator->first, HandleTypes<MappedType>::getFromSlot(m_iterator->second)); } + std::pair<KeyType, HandleSlot> getSlot() const { return *m_iterator; } + + iterator& operator++() { ++m_iterator; return *this; } + + // postfix ++ intentionally omitted + + // Comparison. + bool operator==(const iterator& other) const { return m_iterator == other.m_iterator; } + bool operator!=(const iterator& other) const { return m_iterator != other.m_iterator; } + + private: + map_iterator m_iterator; + }; + + WeakGCMap() + { + } + + bool isEmpty() { return m_map.isEmpty(); } + void clear() + { + map_iterator end = m_map.end(); + for (map_iterator ptr = m_map.begin(); ptr != end; ++ptr) + HandleHeap::heapFor(ptr->second)->deallocate(ptr->second); + m_map.clear(); + } + + bool contains(const KeyType& key) const + { + return m_map.contains(key); + } + + iterator find(const KeyType& key) + { + return m_map.find(key); + } + + void remove(iterator iter) + { + ASSERT(iter.m_iterator != m_map.end()); + HandleSlot slot = iter.m_iterator->second; + ASSERT(slot); + HandleHeap::heapFor(slot)->deallocate(slot); + m_map.remove(iter.m_iterator); + } + + ExternalType get(const KeyType& key) const + { + return HandleTypes<MappedType>::getFromSlot(m_map.get(key)); + } + + HandleSlot getSlot(const KeyType& key) const + { + return m_map.get(key); + } + + pair<iterator, bool> add(JSGlobalData& globalData, const KeyType& key, ExternalType value) + { + pair<typename MapType::iterator, bool> iter = m_map.add(key, 0); + if (iter.second) { + HandleSlot slot = globalData.heap.handleHeap()->allocate(); + iter.first->second = slot; + HandleHeap::heapFor(slot)->makeWeak(slot, this, FinalizerCallback::finalizerContextFor(key)); + HandleHeap::heapFor(slot)->writeBarrier(slot, value); + *slot = value; + } + return iter; + } + + void set(iterator iter, ExternalType value) + { + HandleSlot slot = iter.m_iterator->second; + ASSERT(slot); + HandleHeap::heapFor(slot)->writeBarrier(slot, value); + *slot = value; + } + + void set(JSGlobalData& globalData, const KeyType& key, ExternalType value) + { + pair<typename MapType::iterator, bool> iter = m_map.add(key, 0); + HandleSlot slot = iter.first->second; + if (iter.second) { + slot = globalData.heap.handleHeap()->allocate(); + HandleHeap::heapFor(slot)->makeWeak(slot, this, key); + iter.first->second = slot; + } + HandleHeap::heapFor(slot)->writeBarrier(slot, value); + *slot = value; + } + + ExternalType take(const KeyType& key) + { + HandleSlot slot = m_map.take(key); + if (!slot) + return HashTraits<ExternalType>::emptyValue(); + ExternalType result = HandleTypes<MappedType>::getFromSlot(slot); + HandleHeap::heapFor(slot)->deallocate(slot); + return result; + } + + size_t size() { return m_map.size(); } + + iterator begin() { return iterator(m_map.begin()); } + iterator end() { return iterator(m_map.end()); } + + ~WeakGCMap() + { + clear(); + } + +private: + virtual void finalize(Handle<Unknown> handle, void* context) + { + HandleSlot slot = m_map.take(FinalizerCallback::keyForFinalizer(context, HandleTypes<MappedType>::getFromSlot(handle.slot()))); + ASSERT(slot); + HandleHeap::heapFor(slot)->deallocate(slot); + } + + MapType m_map; +}; + +} // namespace JSC + +#endif // WeakGCMap_h diff --git a/Source/JavaScriptCore/runtime/WeakRandom.h b/Source/JavaScriptCore/runtime/WeakRandom.h new file mode 100644 index 000000000..6083980d2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakRandom.h @@ -0,0 +1,91 @@ +/* + * 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); + } + + unsigned getUint32() + { + return advance(); + } + +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 diff --git a/Source/JavaScriptCore/runtime/WriteBarrier.h b/Source/JavaScriptCore/runtime/WriteBarrier.h new file mode 100644 index 000000000..525fc0926 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WriteBarrier.h @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011 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 WriteBarrier_h +#define WriteBarrier_h + +#include "HandleTypes.h" +#include "Heap.h" +#include "SamplingCounter.h" +#include "TypeTraits.h" + +namespace JSC { + +class JSCell; +class JSGlobalData; +class JSGlobalObject; + +template<class T> class WriteBarrierBase; +template<> class WriteBarrierBase<JSValue>; + +void slowValidateCell(JSCell*); +void slowValidateCell(JSGlobalObject*); + +#if ENABLE(GC_VALIDATION) +template<class T> inline void validateCell(T cell) +{ + ASSERT_GC_OBJECT_INHERITS(cell, &WTF::RemovePointer<T>::Type::s_info); +} + +template<> inline void validateCell<JSCell*>(JSCell* cell) +{ + slowValidateCell(cell); +} + +template<> inline void validateCell<JSGlobalObject*>(JSGlobalObject* globalObject) +{ + slowValidateCell(globalObject); +} +#else +template<class T> inline void validateCell(T) +{ +} +#endif + +// We have a separate base class with no constructors for use in Unions. +template <typename T> class WriteBarrierBase { +public: + void set(JSGlobalData& globalData, const JSCell* owner, T* value) + { + ASSERT(value); + validateCell(value); + setEarlyValue(globalData, owner, value); + } + + void setMayBeNull(JSGlobalData& globalData, const JSCell* owner, T* value) + { + if (value) + validateCell(value); + setEarlyValue(globalData, owner, value); + } + + // Should only be used by JSCell during early initialisation + // when some basic types aren't yet completely instantiated + void setEarlyValue(JSGlobalData&, const JSCell* owner, T* value) + { + this->m_cell = reinterpret_cast<JSCell*>(value); + Heap::writeBarrier(owner, this->m_cell); + } + + T* get() const + { + if (m_cell) + validateCell(m_cell); + return reinterpret_cast<T*>(m_cell); + } + + T* operator*() const + { + ASSERT(m_cell); + validateCell<T>(static_cast<T*>(m_cell)); + return static_cast<T*>(m_cell); + } + + T* operator->() const + { + ASSERT(m_cell); + validateCell(static_cast<T*>(m_cell)); + return static_cast<T*>(m_cell); + } + + void clear() { m_cell = 0; } + + JSCell** slot() { return &m_cell; } + + typedef T* (WriteBarrierBase::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const { return m_cell ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0; } + + bool operator!() const { return !m_cell; } + + void setWithoutWriteBarrier(T* value) + { +#if ENABLE(WRITE_BARRIER_PROFILING) + WriteBarrierCounters::usesWithoutBarrierFromCpp.count(); +#endif + this->m_cell = reinterpret_cast<JSCell*>(value); + } + +#if ENABLE(GC_VALIDATION) + T* unvalidatedGet() const { return reinterpret_cast<T*>(m_cell); } +#endif + +private: + JSCell* m_cell; +}; + +template <> class WriteBarrierBase<Unknown> { +public: + void set(JSGlobalData&, const JSCell* owner, JSValue value) + { + m_value = JSValue::encode(value); + Heap::writeBarrier(owner, value); + } + + void setWithoutWriteBarrier(JSValue value) + { + m_value = JSValue::encode(value); + } + + JSValue get() const + { + return JSValue::decode(m_value); + } + void clear() { m_value = JSValue::encode(JSValue()); } + void setUndefined() { m_value = JSValue::encode(jsUndefined()); } + bool isNumber() const { return get().isNumber(); } + bool isObject() const { return get().isObject(); } + bool isNull() const { return get().isNull(); } + bool isGetterSetter() const { return get().isGetterSetter(); } + + JSValue* slot() + { + union { + EncodedJSValue* v; + JSValue* slot; + } u; + u.v = &m_value; + return u.slot; + } + + typedef JSValue (WriteBarrierBase::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const { return get() ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0; } + bool operator!() const { return !get(); } + +private: + EncodedJSValue m_value; +}; + +template <typename T> class WriteBarrier : public WriteBarrierBase<T> { +public: + WriteBarrier() + { + this->setWithoutWriteBarrier(0); + } + + WriteBarrier(JSGlobalData& globalData, const JSCell* owner, T* value) + { + this->set(globalData, owner, value); + } + + enum MayBeNullTag { MayBeNull }; + WriteBarrier(JSGlobalData& globalData, const JSCell* owner, T* value, MayBeNullTag) + { + this->setMayBeNull(globalData, owner, value); + } +}; + +template <> class WriteBarrier<Unknown> : public WriteBarrierBase<Unknown> { +public: + WriteBarrier() + { + this->setWithoutWriteBarrier(JSValue()); + } + + WriteBarrier(JSGlobalData& globalData, const JSCell* owner, JSValue value) + { + this->set(globalData, owner, value); + } +}; + +template <typename U, typename V> inline bool operator==(const WriteBarrierBase<U>& lhs, const WriteBarrierBase<V>& rhs) +{ + return lhs.get() == rhs.get(); +} + +// MarkStack functions + +template<typename T> inline void MarkStack::append(WriteBarrierBase<T>* slot) +{ + internalAppend(*slot->slot()); +} + +ALWAYS_INLINE void MarkStack::appendValues(WriteBarrierBase<Unknown>* barriers, size_t count) +{ + append(barriers->slot(), count); +} + +} // namespace JSC + +#endif // WriteBarrier_h |