diff options
author | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
---|---|---|
committer | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
commit | d8543bb6618c17b12da906afa77d216f58cf4058 (patch) | |
tree | c58dc05ed86825bd0ef8d305d58c8205106b540f /JavaScriptCore/kjs | |
download | external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2 |
external/webkit r30707
Diffstat (limited to 'JavaScriptCore/kjs')
101 files changed, 36141 insertions, 0 deletions
diff --git a/JavaScriptCore/kjs/Activation.h b/JavaScriptCore/kjs/Activation.h new file mode 100644 index 0000000..9291089 --- /dev/null +++ b/JavaScriptCore/kjs/Activation.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007 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 Activation_h +#define Activation_h + +#include "ExecState.h" +#include "JSVariableObject.h" +#include "object.h" + +namespace KJS { + + class Arguments; + class FunctionImp; + + class ActivationImp : public JSVariableObject { + friend class JSGlobalObject; + friend struct StackActivation; + private: + struct ActivationData : public JSVariableObjectData { + ActivationData() : isOnStack(true), leftRelic(false) { } + ActivationData(const ActivationData&); + + ExecState* exec; + FunctionImp* function; + Arguments* argumentsObject; + + bool isOnStack : 1; + bool leftRelic : 1; + }; + + public: + ActivationImp() { } + ActivationImp(const ActivationData&, bool); + + virtual ~ActivationImp(); + + void init(ExecState*); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void put(ExecState*, const Identifier&, JSValue*); + virtual void initializeVariable(ExecState*, const Identifier&, JSValue*, unsigned attributes); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + virtual void mark(); + void markChildren(); + + virtual bool isActivationObject() const { return true; } + + bool isOnStack() const { return d()->isOnStack; } + bool needsPop() const { return d()->isOnStack || d()->leftRelic; } + + private: + static PropertySlot::GetValueFunc getArgumentsGetter(); + static JSValue* argumentsGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + void createArgumentsObject(ExecState*); + ActivationData* d() const { return static_cast<ActivationData*>(JSVariableObject::d); } + }; + + const size_t activationStackNodeSize = 32; + + struct StackActivation { + StackActivation() { activationStorage.JSVariableObject::d = &activationDataStorage; } + StackActivation(const StackActivation&); + + ActivationImp activationStorage; + ActivationImp::ActivationData activationDataStorage; + }; + + struct ActivationStackNode { + ActivationStackNode* prev; + StackActivation data[activationStackNodeSize]; + }; + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/AllInOneFile.cpp b/JavaScriptCore/kjs/AllInOneFile.cpp new file mode 100644 index 0000000..5144521 --- /dev/null +++ b/JavaScriptCore/kjs/AllInOneFile.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2006 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. + * + */ + +// This file exists to help compile the essential code of +// JavaScriptCore all as one file, for compilers and build systems +// that see a significant speed gain from this. + +#define KDE_USE_FINAL 1 +#include "config.h" + +#include "function.cpp" +#include "debugger.cpp" +#include "array_instance.cpp" +#include "array_object.cpp" +#include "bool_object.cpp" +#include "collector.cpp" +#if PLATFORM(DARWIN) +#include "CollectorHeapIntrospector.cpp" +#endif +#include "CommonIdentifiers.cpp" +#include "date_object.cpp" +#include "DateMath.cpp" +#include "dtoa.cpp" +#include "error_object.cpp" +#include "ExecState.cpp" +#include "function_object.cpp" +#include "grammar.cpp" +#include "identifier.cpp" +#include "internal.cpp" +#include "interpreter.cpp" +#include "JSImmediate.cpp" +#include "JSLock.cpp" +#include "JSWrapperObject.cpp" +#include "lexer.cpp" +#include "list.cpp" +#include "lookup.cpp" +#include "math_object.cpp" +#include "nodes.cpp" +#include "nodes2string.cpp" +#include "number_object.cpp" +#include "object.cpp" +#include "object_object.cpp" +#include "operations.cpp" +#include "Parser.cpp" +#include "property_map.cpp" +#include "property_slot.cpp" +#include "PropertyNameArray.cpp" +#include "regexp.cpp" +#include "regexp_object.cpp" +#include "scope_chain.cpp" +#include "string_object.cpp" +#include "ustring.cpp" +#include "value.cpp" +#include "wtf/FastMalloc.cpp" +#include "wtf/TCSystemAlloc.cpp" diff --git a/JavaScriptCore/kjs/CollectorHeapIntrospector.cpp b/JavaScriptCore/kjs/CollectorHeapIntrospector.cpp new file mode 100644 index 0000000..29ee738 --- /dev/null +++ b/JavaScriptCore/kjs/CollectorHeapIntrospector.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2007 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 "CollectorHeapIntrospector.h" + +#include "collector.h" +#include "MallocZoneSupport.h" + +using WTF::RemoteMemoryReader; + +namespace KJS { + +extern "C" { +malloc_introspection_t jscore_collector_introspection = { &CollectorHeapIntrospector::enumerate, &CollectorHeapIntrospector::goodSize, &CollectorHeapIntrospector::check, &CollectorHeapIntrospector::print, + &CollectorHeapIntrospector::log, &CollectorHeapIntrospector::forceLock, &CollectorHeapIntrospector::forceUnlock, &CollectorHeapIntrospector::statistics }; +} + +void CollectorHeapIntrospector::init(CollectorHeap* primaryHeap, CollectorHeap* numberHeap) +{ + static CollectorHeapIntrospector zone(primaryHeap, numberHeap); +} + +CollectorHeapIntrospector::CollectorHeapIntrospector(CollectorHeap* primaryHeap, CollectorHeap* numberHeap) + : m_primaryHeap(primaryHeap) + , m_numberHeap(numberHeap) +{ + memset(&m_zone, 0, sizeof(m_zone)); + m_zone.zone_name = "JavaScriptCore Collector"; + m_zone.size = &CollectorHeapIntrospector::size; + m_zone.malloc = &CollectorHeapIntrospector::zoneMalloc; + m_zone.calloc = &CollectorHeapIntrospector::zoneCalloc; + m_zone.realloc = &CollectorHeapIntrospector::zoneRealloc; + m_zone.free = &CollectorHeapIntrospector::zoneFree; + m_zone.valloc = &CollectorHeapIntrospector::zoneValloc; + m_zone.destroy = &CollectorHeapIntrospector::zoneDestroy; + m_zone.introspect = &jscore_collector_introspection; + malloc_zone_register(&m_zone); +} + +kern_return_t CollectorHeapIntrospector::enumerate(task_t task, void* context, unsigned typeMask, vm_address_t zoneAddress, memory_reader_t reader, vm_range_recorder_t recorder) +{ + RemoteMemoryReader memoryReader(task, reader); + CollectorHeapIntrospector* zone = memoryReader(reinterpret_cast<CollectorHeapIntrospector*>(zoneAddress)); + CollectorHeap* heaps[2] = {memoryReader(zone->m_primaryHeap), memoryReader(zone->m_numberHeap)}; + + if (!heaps[0]->blocks && !heaps[1]->blocks) + return 0; + + for (int currentHeap = 0; currentHeap < 2; currentHeap++) { + CollectorHeap* heap = heaps[currentHeap]; + CollectorBlock** blocks = memoryReader(heap->blocks); + for (unsigned i = 0; i < heap->usedBlocks; i++) { + vm_address_t remoteBlockAddress = reinterpret_cast<vm_address_t>(blocks[i]); + vm_range_t ptrRange = { remoteBlockAddress, sizeof(CollectorBlock) }; + + if (typeMask & (MALLOC_PTR_REGION_RANGE_TYPE | MALLOC_ADMIN_REGION_RANGE_TYPE)) + (*recorder)(task, context, MALLOC_PTR_REGION_RANGE_TYPE, &ptrRange, 1); + + // Recording individual cells causes frequent false-positives. Any garbage cells + // which have yet to be collected are labeled as leaks. Recording on a per-block + // basis provides less detail but avoids these false-positives. + if (memoryReader(blocks[i])->usedCells && (typeMask & MALLOC_PTR_IN_USE_RANGE_TYPE)) + (*recorder)(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, &ptrRange, 1); + } + } + + return 0; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/CollectorHeapIntrospector.h b/JavaScriptCore/kjs/CollectorHeapIntrospector.h new file mode 100644 index 0000000..76ba324 --- /dev/null +++ b/JavaScriptCore/kjs/CollectorHeapIntrospector.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2007 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 CollectorHeapIntrospector_h +#define CollectorHeapIntrospector_h + +#include <malloc/malloc.h> +#include "Assertions.h" + +namespace KJS { + +struct CollectorHeap; + +class CollectorHeapIntrospector { +public: + static void init(CollectorHeap*, CollectorHeap*); + static kern_return_t enumerate(task_t, void* context, unsigned typeMask, vm_address_t zoneAddress, memory_reader_t, vm_range_recorder_t); + static size_t goodSize(malloc_zone_t*, size_t size) { return size; } + static boolean_t check(malloc_zone_t*) { return true; } + static void print(malloc_zone_t*, boolean_t) { } + static void log(malloc_zone_t*, void*) { } + static void forceLock(malloc_zone_t*) { } + static void forceUnlock(malloc_zone_t*) { } + static void statistics(malloc_zone_t*, malloc_statistics_t*) { } + +private: + CollectorHeapIntrospector(CollectorHeap*, CollectorHeap*); + static size_t size(malloc_zone_t*, const void*) { return 0; } + static void* zoneMalloc(malloc_zone_t*, size_t) { LOG_ERROR("malloc is not supported"); return 0; } + static void* zoneCalloc(malloc_zone_t*, size_t, size_t) { LOG_ERROR("calloc is not supported"); return 0; } + static void* zoneRealloc(malloc_zone_t*, void*, size_t) { LOG_ERROR("realloc is not supported"); return 0; } + static void* zoneValloc(malloc_zone_t*, size_t) { LOG_ERROR("valloc is not supported"); return 0; } + static void zoneDestroy(malloc_zone_t*) { } + + static void zoneFree(malloc_zone_t*, void* ptr) + { + // Due to <rdar://problem/5671357> zoneFree may be called by the system free even if the pointer + // is not in this zone. When this happens, the pointer being freed was not allocated by any + // zone so we need to print a useful error for the application developer. + malloc_printf("*** error for object %p: pointer being freed was not allocated\n", ptr); + } + + malloc_zone_t m_zone; + CollectorHeap* m_primaryHeap; + CollectorHeap* m_numberHeap; +}; + +} + +#endif // CollectorHeapIntrospector_h diff --git a/JavaScriptCore/kjs/CommonIdentifiers.cpp b/JavaScriptCore/kjs/CommonIdentifiers.cpp new file mode 100644 index 0000000..5f673b8 --- /dev/null +++ b/JavaScriptCore/kjs/CommonIdentifiers.cpp @@ -0,0 +1,47 @@ +/* + * 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. + * + */ + +#include "config.h" +#include "CommonIdentifiers.h" + +namespace KJS { + +const char* const nullCString = 0; + +#define INITIALIZE_PROPERTY_NAME(name) , name ( #name ) + +CommonIdentifiers::CommonIdentifiers() + : nullIdentifier(nullCString) + , underscoreProto("__proto__") + KJS_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) +{ +} + +CommonIdentifiers* CommonIdentifiers::shared() +{ + static CommonIdentifiers* sharedInstance; + if (!sharedInstance) { + JSLock lock; + sharedInstance = new CommonIdentifiers; + } + return sharedInstance; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/CommonIdentifiers.h b/JavaScriptCore/kjs/CommonIdentifiers.h new file mode 100644 index 0000000..ae5c778 --- /dev/null +++ b/JavaScriptCore/kjs/CommonIdentifiers.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2003,2007 Apple Computer, 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. + * + */ + +#ifndef KJS_COMMON_IDENTIFIERS_H +#define KJS_COMMON_IDENTIFIERS_H + +#include "identifier.h" +#include <wtf/Noncopyable.h> + +// List of property names, passed to a macro so we can do set them up various +// ways without repeating the list. +#define KJS_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \ + macro(arguments) \ + macro(callee) \ + macro(caller) \ + macro(constructor) \ + macro(fromCharCode) \ + macro(global) \ + macro(ignoreCase) \ + macro(index) \ + macro(input) \ + macro(length) \ + macro(message) \ + macro(multiline) \ + macro(name) \ + macro(prototype) \ + macro(source) \ + macro(toExponential) \ + macro(toFixed) \ + macro(toLocaleString) \ + macro(toPrecision) \ + macro(toString) \ + macro(valueOf) + +namespace KJS { + + class CommonIdentifiers : Noncopyable { + + private: + CommonIdentifiers(); + + public: + static CommonIdentifiers* shared(); + + const Identifier nullIdentifier; + const Identifier underscoreProto; + +#define KJS_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL(name) const Identifier name; + KJS_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(KJS_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL) +#undef KJS_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL + }; +} // namespace KJS + +#endif // KJS_COMMON_IDENTIFIERS_H + diff --git a/JavaScriptCore/kjs/DateMath.cpp b/JavaScriptCore/kjs/DateMath.cpp new file mode 100644 index 0000000..22816b3 --- /dev/null +++ b/JavaScriptCore/kjs/DateMath.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple 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 "DateMath.h" + +#include <math.h> +#include <stdint.h> +#include <value.h> + +#include <wtf/Assertions.h> + +#if PLATFORM(DARWIN) +#include <notify.h> +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +namespace KJS { + +/* Constants */ + +static const double minutesPerDay = 24.0 * 60.0; +static const double secondsPerDay = 24.0 * 60.0 * 60.0; +static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0; + +static const double usecPerSec = 1000000.0; + +static const double maxUnixTime = 2145859200.0; // 12/31/2037 + +// Day of year for the first day of each month, where index 0 is January, and day 0 is January 1. +// First for non-leap years, then for leap years. +static const int firstDayOfMonth[2][12] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} +}; + +static inline bool isLeapYear(int year) +{ + if (year % 4 != 0) + return false; + if (year % 400 == 0) + return true; + if (year % 100 == 0) + return false; + return true; +} + +static inline int daysInYear(int year) +{ + return 365 + isLeapYear(year); +} + +static inline double daysFrom1970ToYear(int year) +{ + // The Gregorian Calendar rules for leap years: + // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years. + // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years. + // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years. + + static const int leapDaysBefore1971By4Rule = 1970 / 4; + static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100; + static const int leapDaysBefore1971By400Rule = 1970 / 400; + + const double yearMinusOne = year - 1; + const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule; + const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule; + const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule; + + return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; +} + +static inline double msToDays(double ms) +{ + return floor(ms / msPerDay); +} + +static inline int msToYear(double ms) +{ + int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970); + double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); + if (msFromApproxYearTo1970 > ms) + return approxYear - 1; + if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms) + return approxYear + 1; + return approxYear; +} + +static inline int dayInYear(double ms, int year) +{ + return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year)); +} + +static inline double msToMilliseconds(double ms) +{ + double result = fmod(ms, msPerDay); + if (result < 0) + result += msPerDay; + return 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; +} + +static inline int msToSeconds(double ms) +{ + double result = fmod(floor(ms / msPerSecond), secondsPerMinute); + if (result < 0) + result += secondsPerMinute; + return static_cast<int>(result); +} + +static inline int msToMinutes(double ms) +{ + double result = fmod(floor(ms / msPerMinute), minutesPerHour); + if (result < 0) + result += minutesPerHour; + return static_cast<int>(result); +} + +static inline int msToHours(double ms) +{ + double result = fmod(floor(ms/msPerHour), hoursPerDay); + if (result < 0) + result += hoursPerDay; + return static_cast<int>(result); +} + +static inline int monthFromDayInYear(int dayInYear, bool leapYear) +{ + const int d = dayInYear; + int step; + + if (d < (step = 31)) + return 0; + step += (leapYear ? 29 : 28); + if (d < step) + return 1; + if (d < (step += 31)) + return 2; + if (d < (step += 30)) + return 3; + if (d < (step += 31)) + return 4; + if (d < (step += 30)) + return 5; + if (d < (step += 31)) + return 6; + if (d < (step += 31)) + return 7; + if (d < (step += 30)) + return 8; + if (d < (step += 31)) + return 9; + if (d < (step += 30)) + return 10; + return 11; +} + +static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) +{ + startDayOfThisMonth = startDayOfNextMonth; + startDayOfNextMonth += daysInThisMonth; + return (dayInYear <= startDayOfNextMonth); +} + +static inline int dayInMonthFromDayInYear(int dayInYear, bool leapYear) +{ + const int d = dayInYear; + int step; + int next = 30; + + if (d <= next) + return d + 1; + const int daysInFeb = (leapYear ? 29 : 28); + if (checkMonth(d, step, next, daysInFeb)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + step = next; + return d - step; +} + +static inline int monthToDayInYear(int month, bool isLeapYear) +{ + return firstDayOfMonth[isLeapYear][month]; +} + +static inline double timeToMS(double hour, double min, double sec, double ms) +{ + return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms); +} + +static int dateToDayInYear(int year, int month, int day) +{ + year += month / 12; + + month %= 12; + if (month < 0) { + month += 12; + --year; + } + + int yearday = static_cast<int>(floor(daysFrom1970ToYear(year))); + int monthday = monthToDayInYear(month, isLeapYear(year)); + + return yearday + monthday + day - 1; +} + +double getCurrentUTCTime() +{ +#if PLATFORM(WIN_OS) +#if COMPILER(BORLAND) + struct timeb timebuffer; + ftime(&timebuffer); +#else + struct _timeb timebuffer; + _ftime(&timebuffer); +#endif + double utc = timebuffer.time * msPerSecond + timebuffer.millitm; +#else + struct timeval tv; + gettimeofday(&tv, 0); + double utc = floor(tv.tv_sec * msPerSecond + tv.tv_usec / 1000); +#endif + return utc; +} + +// There is a hard limit at 2038 that we currently do not have a workaround +// for (rdar://problem/5052975). +static inline int maximumYearForDST() +{ + return 2037; +} + +// It is ok if the cached year is not the current year (e.g. Dec 31st) +// so long as the rules for DST did not change between the two years, if it does +// the app would need to be restarted. +static int mimimumYearForDST() +{ + // Because of the 2038 issue (see maximumYearForDST) if the current year is + // greater than the max year minus 27 (2010), we want to use the max year + // minus 27 instead, to ensure there is a range of 28 years that all years + // can map to. + static int minYear = std::min(msToYear(getCurrentUTCTime()), maximumYearForDST() - 27) ; + return minYear; +} + +/* + * Find an equivalent year for the one given, where equivalence is deterined by + * the two years having the same leapness and the first day of the year, falling + * on the same day of the week. + * + * This function returns a year between this current year and 2037, however this + * function will potentially return incorrect results if the current year is after + * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after + * 2100, (rdar://problem/5055038). + */ +int equivalentYearForDST(int year) +{ + static int minYear = mimimumYearForDST(); + static int maxYear = maximumYearForDST(); + + int difference; + if (year > maxYear) + difference = minYear - year; + else if (year < minYear) + difference = maxYear - year; + else + return year; + + int quotient = difference / 28; + int product = (quotient) * 28; + + year += product; + ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(NaN))); + return year; +} + +/* + * Get the difference in milliseconds between this time zone and UTC (GMT) + * NOT including DST. + */ +double getUTCOffset() +{ +#if PLATFORM(DARWIN) + // Register for a notification whenever the time zone changes. + static bool triedToRegister = false; + static bool haveNotificationToken = false; + static int notificationToken; + if (!triedToRegister) { + triedToRegister = true; + uint32_t status = notify_register_check("com.apple.system.timezone", ¬ificationToken); + if (status == NOTIFY_STATUS_OK) + haveNotificationToken = true; + } + + // If we can verify that we have not received a time zone notification, + // then use the cached offset from the last time this function was called. + static bool haveCachedOffset = false; + static double cachedOffset; + if (haveNotificationToken && haveCachedOffset) { + int notified; + uint32_t status = notify_check(notificationToken, ¬ified); + if (status == NOTIFY_STATUS_OK && !notified) + return cachedOffset; + } +#endif + + tm localt; + + memset(&localt, 0, sizeof(localt)); + + // get the difference between this time zone and UTC on Jan 01, 2000 12:00:00 AM + localt.tm_mday = 1; + localt.tm_year = 100; + double utcOffset = 946684800.0 - mktime(&localt); + + utcOffset *= msPerSecond; + +#if PLATFORM(DARWIN) + haveCachedOffset = true; + cachedOffset = utcOffset; +#endif + + return utcOffset; +} + +/* + * Get the DST offset for the time passed in. Takes + * seconds (not milliseconds) and cannot handle dates before 1970 + * on some OS' + */ +static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset) +{ + if (localTimeSeconds > maxUnixTime) + localTimeSeconds = maxUnixTime; + else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0) + localTimeSeconds += secondsPerDay; + + //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset() + double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset; + + // Offset from UTC but doesn't include DST obviously + int offsetHour = msToHours(offsetTime); + int offsetMinute = msToMinutes(offsetTime); + + // FIXME: time_t has a potential problem in 2038 + time_t localTime = static_cast<time_t>(localTimeSeconds); + + tm localTM; +#if PLATFORM(QT) + // ### this is not threadsafe but we don't use multiple threads anyway + // in the Qt build +#if USE(MULTIPLE_THREADS) +#error Mulitple threads are currently not supported in the Qt/mingw build +#endif + localTM = *localtime(&localTime); +#elif PLATFORM(WIN_OS) + #if COMPILER(MSVC7) + localTM = *localtime(&localTime); + #else + localtime_s(&localTM, &localTime); + #endif +#else + localtime_r(&localTime, &localTM); +#endif + + double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60); + + if (diff < 0) + diff += secondsPerDay; + + return (diff * msPerSecond); +} + +// Get the DST offset, given a time in UTC +static double getDSTOffset(double ms, double utcOffset) +{ + // On Mac OS X, the call to localtime (see getDSTOffsetSimple) will return historically accurate + // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript + // standard explicitly dictates that historical information should not be considered when + // determining DST. For this reason we shift away from years that localtime can handle but would + // return historically accurate information. + int year = msToYear(ms); + int equivalentYear = equivalentYearForDST(year); + if (year != equivalentYear) { + bool leapYear = isLeapYear(year); + int dayInYearLocal = dayInYear(ms, year); + int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear); + int month = monthFromDayInYear(dayInYearLocal, leapYear); + int day = dateToDayInYear(equivalentYear, month, dayInMonth); + ms = (day * msPerDay) + msToMilliseconds(ms); + } + + return getDSTOffsetSimple(ms / msPerSecond, utcOffset); +} + +double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) +{ + int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay); + double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); + double result = (day * msPerDay) + ms; + + if (!inputIsUTC) { // convert to UTC + double utcOffset = getUTCOffset(); + result -= utcOffset; + result -= getDSTOffset(result, utcOffset); + } + + return result; +} + +void msToGregorianDateTime(double ms, bool outputIsUTC, GregorianDateTime& tm) +{ + // input is UTC + double dstOff = 0.0; + const double utcOff = getUTCOffset(); + + if (!outputIsUTC) { // convert to local time + dstOff = getDSTOffset(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) / msPerSecond); + tm.timeZone = NULL; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/DateMath.h b/JavaScriptCore/kjs/DateMath.h new file mode 100644 index 0000000..6fdad0b --- /dev/null +++ b/JavaScriptCore/kjs/DateMath.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple 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 DateMath_h +#define DateMath_h + +#include <time.h> +#include <string.h> +#include <wtf/Noncopyable.h> + +namespace KJS { + +struct GregorianDateTime; + +void msToGregorianDateTime(double, bool outputIsUTC, GregorianDateTime&); +double gregorianDateTimeToMS(const GregorianDateTime&, double, bool inputIsUTC); +double getUTCOffset(); +int equivalentYearForDST(int year); +double getCurrentUTCTime(); + +const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; +const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +const double hoursPerDay = 24.0; +const double minutesPerHour = 60.0; +const double secondsPerHour = 60.0 * 60.0; +const double secondsPerMinute = 60.0; +const double msPerSecond = 1000.0; +const double msPerMinute = 60.0 * 1000.0; +const double msPerHour = 60.0 * 60.0 * 1000.0; +const double msPerDay = 24.0 * 60.0 * 60.0 * 1000.0; + +// Intentionally overridding the default tm of the system +// Tee members of tm differ on various operating systems. +struct GregorianDateTime : Noncopyable { + GregorianDateTime() + : second(0) + , minute(0) + , hour(0) + , weekDay(0) + , monthDay(0) + , yearDay(0) + , month(0) + , year(0) + , isDST(0) + , utcOffset(0) + , timeZone(0) + { + } + + ~GregorianDateTime() + { + delete [] timeZone; + } + + GregorianDateTime(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) + { +#if !PLATFORM(WIN_OS) && !PLATFORM(SOLARIS) + utcOffset = static_cast<int>(inTm.tm_gmtoff); + + int inZoneSize = strlen(inTm.tm_zone) + 1; + timeZone = new char[inZoneSize]; + strncpy(timeZone, inTm.tm_zone, inZoneSize); +#else + utcOffset = static_cast<int>(getUTCOffset() / msPerSecond + (isDST ? secondsPerHour : 0)); + timeZone = 0; +#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 !PLATFORM(WIN_OS) && !PLATFORM(SOLARIS) + ret.tm_gmtoff = static_cast<long>(utcOffset); + ret.tm_zone = timeZone; +#endif + + return ret; + } + + int second; + int minute; + int hour; + int weekDay; + int monthDay; + int yearDay; + int month; + int year; + int isDST; + int utcOffset; + char* timeZone; +}; + +} //namespace KJS + +#endif // DateMath_h diff --git a/JavaScriptCore/kjs/ExecState.cpp b/JavaScriptCore/kjs/ExecState.cpp new file mode 100644 index 0000000..b37523e --- /dev/null +++ b/JavaScriptCore/kjs/ExecState.cpp @@ -0,0 +1,215 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * 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 "ExecState.h" + +#include "Activation.h" +#include "JSGlobalObject.h" +#include "function.h" +#include "internal.h" +#include "scope_chain_mark.h" + +namespace KJS { + +static inline List* globalEmptyList() +{ + static List staticEmptyList; + return &staticEmptyList; +} + +// ECMA 10.2 + +// The constructor for the globalExec pseudo-ExecState +inline ExecState::ExecState(JSGlobalObject* globalObject) + : m_globalObject(globalObject) + , m_exception(0) + , m_propertyNames(CommonIdentifiers::shared()) + , m_emptyList(globalEmptyList()) + , m_callingExec(0) + , m_scopeNode(0) + , m_function(0) + , m_arguments(0) + , m_activation(0) + , m_localStorage(&globalObject->localStorage()) + , m_variableObject(globalObject) + , m_thisValue(globalObject) + , m_iterationDepth(0) + , m_switchDepth(0) + , m_codeType(GlobalCode) +{ + m_scopeChain.push(globalObject); +} + +inline ExecState::ExecState(JSGlobalObject* globalObject, JSObject* /*thisObject*/, ProgramNode* programNode) + : m_globalObject(globalObject) + , m_exception(0) + , m_propertyNames(CommonIdentifiers::shared()) + , m_emptyList(globalEmptyList()) + , m_callingExec(0) + , m_scopeNode(programNode) + , m_function(0) + , m_arguments(0) + , m_activation(0) + , m_localStorage(&globalObject->localStorage()) + , m_variableObject(globalObject) + , m_thisValue(globalObject) + , m_iterationDepth(0) + , m_switchDepth(0) + , m_codeType(GlobalCode) +{ + // FIXME: This function ignores the "thisObject" parameter, which means that the API for evaluating + // a script with a this object that's not the same as the global object is broken, and probably + // has been for some time. + ASSERT(m_scopeNode); + m_scopeChain.push(globalObject); +} + +inline ExecState::ExecState(JSGlobalObject* globalObject, EvalNode* evalNode, ExecState* callingExec) + : m_globalObject(globalObject) + , m_exception(0) + , m_propertyNames(callingExec->m_propertyNames) + , m_emptyList(callingExec->m_emptyList) + , m_callingExec(callingExec) + , m_scopeNode(evalNode) + , m_function(0) + , m_arguments(0) + , m_activation(0) + , m_localStorage(callingExec->m_localStorage) + , m_scopeChain(callingExec->m_scopeChain) + , m_variableObject(callingExec->m_variableObject) + , m_thisValue(callingExec->m_thisValue) + , m_iterationDepth(0) + , m_switchDepth(0) + , m_codeType(EvalCode) +{ + ASSERT(m_scopeNode); +} + +inline ExecState::ExecState(JSGlobalObject* globalObject, JSObject* thisObject, + FunctionBodyNode* functionBodyNode, ExecState* callingExec, + FunctionImp* func, const List& args) + : m_globalObject(globalObject) + , m_exception(0) + , m_propertyNames(callingExec->m_propertyNames) + , m_emptyList(callingExec->m_emptyList) + , m_callingExec(callingExec) + , m_scopeNode(functionBodyNode) + , m_function(func) + , m_arguments(&args) + , m_scopeChain(func->scope()) + , m_thisValue(thisObject) + , m_iterationDepth(0) + , m_switchDepth(0) + , m_codeType(FunctionCode) +{ + ASSERT(m_scopeNode); + + ActivationImp* activation = globalObject->pushActivation(this); + m_activation = activation; + m_localStorage = &activation->localStorage(); + m_variableObject = activation; + m_scopeChain.push(activation); +} + +inline ExecState::~ExecState() +{ +} + +JSGlobalObject* ExecState::lexicalGlobalObject() const +{ + JSObject* object = m_scopeChain.bottom(); + if (object && object->isGlobalObject()) + return static_cast<JSGlobalObject*>(object); + return m_globalObject; +} + +void ExecState::markActiveExecStates() +{ + ExecStateStack::const_iterator end = activeExecStates().end(); + for (ExecStateStack::const_iterator it = activeExecStates().begin(); it != end; ++it) + (*it)->m_scopeChain.mark(); +} + +static inline ExecStateStack& inlineActiveExecStates() +{ + static ExecStateStack staticActiveExecStates; + return staticActiveExecStates; +} + +ExecStateStack& ExecState::activeExecStates() +{ + return inlineActiveExecStates(); +} + +GlobalExecState::GlobalExecState(JSGlobalObject* globalObject) + : ExecState(globalObject) +{ +} + +GlobalExecState::~GlobalExecState() +{ +} + +InterpreterExecState::InterpreterExecState(JSGlobalObject* globalObject, JSObject* thisObject, ProgramNode* programNode) + : ExecState(globalObject, thisObject, programNode) +{ + inlineActiveExecStates().append(this); +} + +InterpreterExecState::~InterpreterExecState() +{ + ASSERT(inlineActiveExecStates().last() == this); + inlineActiveExecStates().removeLast(); +} + +EvalExecState::EvalExecState(JSGlobalObject* globalObject, EvalNode* evalNode, ExecState* callingExec) + : ExecState(globalObject, evalNode, callingExec) +{ + inlineActiveExecStates().append(this); +} + +EvalExecState::~EvalExecState() +{ + ASSERT(inlineActiveExecStates().last() == this); + inlineActiveExecStates().removeLast(); +} + +FunctionExecState::FunctionExecState(JSGlobalObject* globalObject, JSObject* thisObject, + FunctionBodyNode* functionBodyNode, ExecState* callingExec, + FunctionImp* func, const List& args) + : ExecState(globalObject, thisObject, functionBodyNode, callingExec, func, args) +{ + inlineActiveExecStates().append(this); +} + +FunctionExecState::~FunctionExecState() +{ + ASSERT(inlineActiveExecStates().last() == this); + inlineActiveExecStates().removeLast(); + + if (m_activation->needsPop()) + m_globalObject->popActivation(); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/ExecState.h b/JavaScriptCore/kjs/ExecState.h new file mode 100644 index 0000000..ffa067d --- /dev/null +++ b/JavaScriptCore/kjs/ExecState.h @@ -0,0 +1,235 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * 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. + * + */ + +#ifndef ExecState_h +#define ExecState_h + +#include "LabelStack.h" +#include "LocalStorage.h" +#include "completion.h" +#include "list.h" +#include "scope_chain.h" + +namespace KJS { + + class ActivationImp; + class CommonIdentifiers; + class EvalNode; + class FunctionBodyNode; + class FunctionImp; + class GlobalFuncImp; + class Interpreter; + class JSGlobalObject; + class JSVariableObject; + class ProgramNode; + class ScopeNode; + + enum CodeType { GlobalCode, EvalCode, FunctionCode }; + + typedef Vector<ExecState*, 16> ExecStateStack; + + // Represents the current state of script execution. + // Passed as the first argument to most functions. + class ExecState : Noncopyable { + public: + // Global object that was in scope when the current script started executing. + JSGlobalObject* dynamicGlobalObject() const { return m_globalObject; } + + // Global object that was in scope when the current body of code was defined. + JSGlobalObject* lexicalGlobalObject() const; + + void setException(JSValue* exception) { m_exception = exception; } + void clearException() { m_exception = 0; } + JSValue* exception() const { return m_exception; } + JSValue** exceptionSlot() { return &m_exception; } + bool hadException() const { return !!m_exception; } + JSValue* takeException() { JSValue* exception = m_exception; m_exception = 0; return exception; } + + const ScopeChain& scopeChain() const { return m_scopeChain; } + void pushScope(JSObject* s) { m_scopeChain.push(s); } + void popScope() { m_scopeChain.pop(); } + void replaceScopeChainTop(JSObject* o) { m_scopeChain.replaceTop(o); } + + JSVariableObject* variableObject() const { return m_variableObject; } + void setVariableObject(JSVariableObject* v) { m_variableObject = v; } + + JSObject* thisValue() const { return m_thisValue; } + + ExecState* callingExecState() { return m_callingExec; } + + ActivationImp* activationObject() { return m_activation; } + void setActivationObject(ActivationImp* a) { m_activation = a; } + CodeType codeType() { return m_codeType; } + ScopeNode* scopeNode() { return m_scopeNode; } + FunctionImp* function() const { return m_function; } + const List* arguments() const { return m_arguments; } + + LabelStack& seenLabels() { return m_labelStack; } + + void pushIteration() { m_iterationDepth++; } + void popIteration() { m_iterationDepth--; } + bool inIteration() const { return (m_iterationDepth > 0); } + + void pushSwitch() { m_switchDepth++; } + void popSwitch() { m_switchDepth--; } + bool inSwitch() const { return (m_switchDepth > 0); } + + // These pointers are used to avoid accessing global variables for these, + // to avoid taking PIC branches in Mach-O binaries. + const CommonIdentifiers& propertyNames() const { return *m_propertyNames; } + const List& emptyList() const { return *m_emptyList; } + + LocalStorage& localStorage() { return *m_localStorage; } + void setLocalStorage(LocalStorage* s) { m_localStorage = s; } + + // These are only valid right after calling execute(). + ComplType completionType() const { return m_completionType; } + const Identifier& breakOrContinueTarget() const + { + ASSERT(m_completionType == Break || m_completionType == Continue); + return *m_breakOrContinueTarget; + } + + // Only for use in the implementation of execute(). + void setCompletionType(ComplType type) + { + ASSERT(type != Break); + ASSERT(type != Continue); + m_completionType = type; + } + JSValue* setNormalCompletion() + { + ASSERT(!hadException()); + m_completionType = Normal; + return 0; + } + JSValue* setNormalCompletion(JSValue* value) + { + ASSERT(!hadException()); + m_completionType = Normal; + return value; + } + JSValue* setBreakCompletion(const Identifier* target) + { + ASSERT(!hadException()); + m_completionType = Break; + m_breakOrContinueTarget = target; + return 0; + } + JSValue* setContinueCompletion(const Identifier* target) + { + ASSERT(!hadException()); + m_completionType = Continue; + m_breakOrContinueTarget = target; + return 0; + } + JSValue* setReturnValueCompletion(JSValue* returnValue) + { + ASSERT(!hadException()); + ASSERT(returnValue); + m_completionType = ReturnValue; + return returnValue; + } + JSValue* setThrowCompletion(JSValue* exception) + { + ASSERT(!hadException()); + ASSERT(exception); + m_completionType = Throw; + return exception; + } + JSValue* setInterruptedCompletion() + { + ASSERT(!hadException()); + m_completionType = Interrupted; + return 0; + } + + static void markActiveExecStates(); + static ExecStateStack& activeExecStates(); + + protected: + ExecState(JSGlobalObject*); + ExecState(JSGlobalObject*, JSObject* thisObject, ProgramNode*); + ExecState(JSGlobalObject*, EvalNode*, ExecState* callingExecState); + ExecState(JSGlobalObject*, JSObject* thisObject, FunctionBodyNode*, + ExecState* callingExecState, FunctionImp*, const List& args); + ~ExecState(); + + // ExecStates are always stack-allocated, and the garbage collector + // marks the stack, so we don't need to protect the objects below from GC. + + JSGlobalObject* m_globalObject; + JSValue* m_exception; + CommonIdentifiers* m_propertyNames; + const List* m_emptyList; + + ExecState* m_callingExec; + + ScopeNode* m_scopeNode; + + FunctionImp* m_function; + const List* m_arguments; + ActivationImp* m_activation; + LocalStorage* m_localStorage; + + ScopeChain m_scopeChain; + JSVariableObject* m_variableObject; + JSObject* m_thisValue; + + LabelStack m_labelStack; + int m_iterationDepth; + int m_switchDepth; + CodeType m_codeType; + + ComplType m_completionType; + const Identifier* m_breakOrContinueTarget; + }; + + class GlobalExecState : public ExecState { + public: + GlobalExecState(JSGlobalObject*); + ~GlobalExecState(); + }; + + class InterpreterExecState : public ExecState { + public: + InterpreterExecState(JSGlobalObject*, JSObject* thisObject, ProgramNode*); + ~InterpreterExecState(); + }; + + class EvalExecState : public ExecState { + public: + EvalExecState(JSGlobalObject*, EvalNode*, ExecState* callingExecState); + ~EvalExecState(); + }; + + class FunctionExecState : public ExecState { + public: + FunctionExecState(JSGlobalObject*, JSObject* thisObject, FunctionBodyNode*, + ExecState* callingExecState, FunctionImp*, const List& args); + ~FunctionExecState(); + }; + +} // namespace KJS + +#endif // ExecState_h diff --git a/JavaScriptCore/kjs/JSGlobalObject.cpp b/JavaScriptCore/kjs/JSGlobalObject.cpp new file mode 100644 index 0000000..aaf11e3 --- /dev/null +++ b/JavaScriptCore/kjs/JSGlobalObject.cpp @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2007, 2008 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 "Activation.h" +#include "array_object.h" +#include "bool_object.h" +#include "date_object.h" +#include "debugger.h" +#include "error_object.h" +#include "function_object.h" +#include "math_object.h" +#include "number_object.h" +#include "object_object.h" +#include "regexp_object.h" +#include "SavedBuiltins.h" +#include "string_object.h" + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if PLATFORM(WIN_OS) +#include <windows.h> +#endif + +#if PLATFORM(QT) +#include <QDateTime> +#endif + +namespace KJS { + +// Default number of ticks before a timeout check should be done. +static const int initialTickCountThreshold = 255; + +// Preferred number of milliseconds between each timeout check +static const int preferredScriptCheckTimeInterval = 1000; + +static inline void markIfNeeded(JSValue* v) +{ + if (v && !v->marked()) + v->mark(); +} + +// Returns the current time in milliseconds +// It doesn't matter what "current time" is here, just as long as +// it's possible to measure the time difference correctly. +static inline unsigned getCurrentTime() +{ +#if HAVE(SYS_TIME_H) + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +#elif PLATFORM(QT) + QDateTime t = QDateTime::currentDateTime(); + return t.toTime_t() * 1000 + t.time().msec(); +#elif PLATFORM(WIN_OS) + return timeGetTime(); +#else +#error Platform does not have getCurrentTime function +#endif +} + +JSGlobalObject* JSGlobalObject::s_head = 0; + +void JSGlobalObject::deleteActivationStack() +{ + ActivationStackNode* prevNode = 0; + for (ActivationStackNode* currentNode = d()->activations; currentNode; currentNode = prevNode) { + prevNode = currentNode->prev; + delete currentNode; + } +} + +JSGlobalObject::~JSGlobalObject() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (d()->debugger) + d()->debugger->detach(this); + + d()->next->d()->prev = d()->prev; + d()->prev->d()->next = d()->next; + s_head = d()->next; + if (s_head == this) + s_head = 0; + + deleteActivationStack(); + + delete d(); +} + +void JSGlobalObject::init() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (s_head) { + d()->prev = s_head; + d()->next = s_head->d()->next; + s_head->d()->next->d()->prev = this; + s_head->d()->next = this; + } else + s_head = d()->next = d()->prev = this; + + resetTimeoutCheck(); + d()->timeoutTime = 0; + d()->timeoutCheckCount = 0; + + d()->recursion = 0; + d()->debugger = 0; + + ActivationStackNode* newStackNode = new ActivationStackNode; + newStackNode->prev = 0; + d()->activations = newStackNode; + d()->activationCount = 0; + + reset(prototype()); +} + +bool JSGlobalObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (symbolTableGet(propertyName, slot)) + return true; + return JSVariableObject::getOwnPropertySlot(exec, propertyName, slot); +} + +void JSGlobalObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + if (symbolTablePut(propertyName, value)) + return; + return JSVariableObject::put(exec, propertyName, value); +} + +void JSGlobalObject::initializeVariable(ExecState* exec, const Identifier& propertyName, JSValue* value, unsigned attributes) +{ + if (symbolTableInitializeVariable(propertyName, value, attributes)) + return; + + JSValue* valueBefore = getDirect(propertyName); + JSVariableObject::put(exec, propertyName, value); + if (!valueBefore) { + if (JSValue* valueAfter = getDirect(propertyName)) + putDirect(propertyName, valueAfter, attributes); + } +} + +static inline JSObject* lastInPrototypeChain(JSObject* object) +{ + JSObject* o = object; + while (o->prototype()->isObject()) + o = static_cast<JSObject*>(o->prototype()); + return o; +} + +void JSGlobalObject::reset(JSValue* prototype) +{ + // Clear before inititalizing, to avoid calling mark() on stale pointers -- + // which would be wasteful -- or uninitialized pointers -- which would be + // dangerous. (The allocations below may cause a GC.) + + _prop.clear(); + localStorage().clear(); + symbolTable().clear(); + + // Prototypes + d()->functionPrototype = 0; + d()->objectPrototype = 0; + + d()->arrayPrototype = 0; + d()->stringPrototype = 0; + d()->booleanPrototype = 0; + d()->numberPrototype = 0; + d()->datePrototype = 0; + d()->regExpPrototype = 0; + d()->errorPrototype = 0; + + d()->evalErrorPrototype = 0; + d()->rangeErrorPrototype = 0; + d()->referenceErrorPrototype = 0; + d()->syntaxErrorPrototype = 0; + d()->typeErrorPrototype = 0; + d()->URIErrorPrototype = 0; + + // Constructors + d()->objectConstructor = 0; + d()->functionConstructor = 0; + d()->arrayConstructor = 0; + d()->stringConstructor = 0; + d()->booleanConstructor = 0; + d()->numberConstructor = 0; + d()->dateConstructor = 0; + d()->regExpConstructor = 0; + d()->errorConstructor = 0; + + d()->evalErrorConstructor = 0; + d()->rangeErrorConstructor = 0; + d()->referenceErrorConstructor = 0; + d()->syntaxErrorConstructor = 0; + d()->typeErrorConstructor = 0; + d()->URIErrorConstructor = 0; + + ExecState* exec = &d()->globalExec; + + // Prototypes + d()->functionPrototype = new FunctionPrototype(exec); + d()->objectPrototype = new ObjectPrototype(exec, d()->functionPrototype); + d()->functionPrototype->setPrototype(d()->objectPrototype); + + d()->arrayPrototype = new ArrayPrototype(exec, d()->objectPrototype); + d()->stringPrototype = new StringPrototype(exec, d()->objectPrototype); + d()->booleanPrototype = new BooleanPrototype(exec, d()->objectPrototype, d()->functionPrototype); + d()->numberPrototype = new NumberPrototype(exec, d()->objectPrototype, d()->functionPrototype); + d()->datePrototype = new DatePrototype(exec, d()->objectPrototype); + d()->regExpPrototype = new RegExpPrototype(exec, d()->objectPrototype, d()->functionPrototype); + d()->errorPrototype = new ErrorPrototype(exec, d()->objectPrototype, d()->functionPrototype); + + d()->evalErrorPrototype = new NativeErrorPrototype(exec, d()->errorPrototype, "EvalError", "EvalError"); + d()->rangeErrorPrototype = new NativeErrorPrototype(exec, d()->errorPrototype, "RangeError", "RangeError"); + d()->referenceErrorPrototype = new NativeErrorPrototype(exec, d()->errorPrototype, "ReferenceError", "ReferenceError"); + d()->syntaxErrorPrototype = new NativeErrorPrototype(exec, d()->errorPrototype, "SyntaxError", "SyntaxError"); + d()->typeErrorPrototype = new NativeErrorPrototype(exec, d()->errorPrototype, "TypeError", "TypeError"); + d()->URIErrorPrototype = new NativeErrorPrototype(exec, d()->errorPrototype, "URIError", "URIError"); + + // Constructors + d()->objectConstructor = new ObjectObjectImp(exec, d()->objectPrototype, d()->functionPrototype); + d()->functionConstructor = new FunctionObjectImp(exec, d()->functionPrototype); + d()->arrayConstructor = new ArrayObjectImp(exec, d()->functionPrototype, d()->arrayPrototype); + d()->stringConstructor = new StringObjectImp(exec, d()->functionPrototype, d()->stringPrototype); + d()->booleanConstructor = new BooleanObjectImp(exec, d()->functionPrototype, d()->booleanPrototype); + d()->numberConstructor = new NumberObjectImp(exec, d()->functionPrototype, d()->numberPrototype); + d()->dateConstructor = new DateObjectImp(exec, d()->functionPrototype, d()->datePrototype); + d()->regExpConstructor = new RegExpObjectImp(exec, d()->functionPrototype, d()->regExpPrototype); + d()->errorConstructor = new ErrorObjectImp(exec, d()->functionPrototype, d()->errorPrototype); + + d()->evalErrorConstructor = new NativeErrorImp(exec, d()->functionPrototype, d()->evalErrorPrototype); + d()->rangeErrorConstructor = new NativeErrorImp(exec, d()->functionPrototype, d()->rangeErrorPrototype); + d()->referenceErrorConstructor = new NativeErrorImp(exec, d()->functionPrototype, d()->referenceErrorPrototype); + d()->syntaxErrorConstructor = new NativeErrorImp(exec, d()->functionPrototype, d()->syntaxErrorPrototype); + d()->typeErrorConstructor = new NativeErrorImp(exec, d()->functionPrototype, d()->typeErrorPrototype); + d()->URIErrorConstructor = new NativeErrorImp(exec, d()->functionPrototype, d()->URIErrorPrototype); + + d()->functionPrototype->putDirect(exec->propertyNames().constructor, d()->functionConstructor, DontEnum); + + d()->objectPrototype->putDirect(exec->propertyNames().constructor, d()->objectConstructor, DontEnum); + d()->functionPrototype->putDirect(exec->propertyNames().constructor, d()->functionConstructor, DontEnum); + d()->arrayPrototype->putDirect(exec->propertyNames().constructor, d()->arrayConstructor, DontEnum); + d()->booleanPrototype->putDirect(exec->propertyNames().constructor, d()->booleanConstructor, DontEnum); + d()->stringPrototype->putDirect(exec->propertyNames().constructor, d()->stringConstructor, DontEnum); + d()->numberPrototype->putDirect(exec->propertyNames().constructor, d()->numberConstructor, DontEnum); + d()->datePrototype->putDirect(exec->propertyNames().constructor, d()->dateConstructor, DontEnum); + d()->regExpPrototype->putDirect(exec->propertyNames().constructor, d()->regExpConstructor, DontEnum); + d()->errorPrototype->putDirect(exec->propertyNames().constructor, d()->errorConstructor, DontEnum); + d()->evalErrorPrototype->putDirect(exec->propertyNames().constructor, d()->evalErrorConstructor, DontEnum); + d()->rangeErrorPrototype->putDirect(exec->propertyNames().constructor, d()->rangeErrorConstructor, DontEnum); + d()->referenceErrorPrototype->putDirect(exec->propertyNames().constructor, d()->referenceErrorConstructor, DontEnum); + d()->syntaxErrorPrototype->putDirect(exec->propertyNames().constructor, d()->syntaxErrorConstructor, DontEnum); + d()->typeErrorPrototype->putDirect(exec->propertyNames().constructor, d()->typeErrorConstructor, DontEnum); + d()->URIErrorPrototype->putDirect(exec->propertyNames().constructor, d()->URIErrorConstructor, DontEnum); + + // Set global constructors + + // FIXME: These properties could be handled by a static hash table. + + putDirect("Object", d()->objectConstructor, DontEnum); + putDirect("Function", d()->functionConstructor, DontEnum); + putDirect("Array", d()->arrayConstructor, DontEnum); + putDirect("Boolean", d()->booleanConstructor, DontEnum); + putDirect("String", d()->stringConstructor, DontEnum); + putDirect("Number", d()->numberConstructor, DontEnum); + putDirect("Date", d()->dateConstructor, DontEnum); + putDirect("RegExp", d()->regExpConstructor, DontEnum); + putDirect("Error", d()->errorConstructor, DontEnum); + putDirect("EvalError", d()->evalErrorConstructor); + putDirect("RangeError", d()->rangeErrorConstructor); + putDirect("ReferenceError", d()->referenceErrorConstructor); + putDirect("SyntaxError", d()->syntaxErrorConstructor); + putDirect("TypeError", d()->typeErrorConstructor); + putDirect("URIError", d()->URIErrorConstructor); + + // Set global values. + + putDirect("Math", new MathObjectImp(exec, d()->objectPrototype), DontEnum); + + putDirect("NaN", jsNaN(), DontEnum | DontDelete); + putDirect("Infinity", jsNumber(Inf), DontEnum | DontDelete); + putDirect("undefined", jsUndefined(), DontEnum | DontDelete); + + // Set global functions. + + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "eval", globalFuncEval), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 2, "parseInt", globalFuncParseInt), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "parseFloat", globalFuncParseFloat), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "isNaN", globalFuncIsNaN), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "isFinite", globalFuncIsFinite), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "escape", globalFuncEscape), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "unescape", globalFuncUnescape), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "decodeURI", globalFuncDecodeURI), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "decodeURIComponent", globalFuncDecodeURIComponent), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "encodeURI", globalFuncEncodeURI), DontEnum); + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "encodeURIComponent", globalFuncEncodeURIComponent), DontEnum); +#ifndef NDEBUG + putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "kjsprint", globalFuncKJSPrint), DontEnum); +#endif + + // Set prototype, and also insert the object prototype at the end of the chain. + + setPrototype(prototype); + lastInPrototypeChain(this)->setPrototype(d()->objectPrototype); +} + +void JSGlobalObject::startTimeoutCheck() +{ + if (!d()->timeoutCheckCount) + resetTimeoutCheck(); + + ++d()->timeoutCheckCount; +} + +void JSGlobalObject::stopTimeoutCheck() +{ + --d()->timeoutCheckCount; +} + +void JSGlobalObject::resetTimeoutCheck() +{ + d()->tickCount = 0; + d()->ticksUntilNextTimeoutCheck = initialTickCountThreshold; + d()->timeAtLastCheckTimeout = 0; + d()->timeExecuting = 0; +} + +bool JSGlobalObject::checkTimeout() +{ + d()->tickCount = 0; + + unsigned currentTime = getCurrentTime(); + + if (!d()->timeAtLastCheckTimeout) { + // Suspicious amount of looping in a script -- start timing it + d()->timeAtLastCheckTimeout = currentTime; + return false; + } + + unsigned timeDiff = currentTime - d()->timeAtLastCheckTimeout; + + if (timeDiff == 0) + timeDiff = 1; + + d()->timeExecuting += timeDiff; + d()->timeAtLastCheckTimeout = currentTime; + + // Adjust the tick threshold so we get the next checkTimeout call in the interval specified in + // preferredScriptCheckTimeInterval + d()->ticksUntilNextTimeoutCheck = (unsigned)((float)preferredScriptCheckTimeInterval / timeDiff) * d()->ticksUntilNextTimeoutCheck; + + // 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 (d()->ticksUntilNextTimeoutCheck == 0) + d()->ticksUntilNextTimeoutCheck = initialTickCountThreshold; + + if (d()->timeoutTime && d()->timeExecuting > d()->timeoutTime) { + if (shouldInterruptScript()) + return true; + + resetTimeoutCheck(); + } + + return false; +} + +void JSGlobalObject::saveBuiltins(SavedBuiltins& builtins) const +{ + if (!builtins._internal) + builtins._internal = new SavedBuiltinsInternal; + + builtins._internal->objectConstructor = d()->objectConstructor; + builtins._internal->functionConstructor = d()->functionConstructor; + builtins._internal->arrayConstructor = d()->arrayConstructor; + builtins._internal->booleanConstructor = d()->booleanConstructor; + builtins._internal->stringConstructor = d()->stringConstructor; + builtins._internal->numberConstructor = d()->numberConstructor; + builtins._internal->dateConstructor = d()->dateConstructor; + builtins._internal->regExpConstructor = d()->regExpConstructor; + builtins._internal->errorConstructor = d()->errorConstructor; + builtins._internal->evalErrorConstructor = d()->evalErrorConstructor; + builtins._internal->rangeErrorConstructor = d()->rangeErrorConstructor; + builtins._internal->referenceErrorConstructor = d()->referenceErrorConstructor; + builtins._internal->syntaxErrorConstructor = d()->syntaxErrorConstructor; + builtins._internal->typeErrorConstructor = d()->typeErrorConstructor; + builtins._internal->URIErrorConstructor = d()->URIErrorConstructor; + + builtins._internal->objectPrototype = d()->objectPrototype; + builtins._internal->functionPrototype = d()->functionPrototype; + builtins._internal->arrayPrototype = d()->arrayPrototype; + builtins._internal->booleanPrototype = d()->booleanPrototype; + builtins._internal->stringPrototype = d()->stringPrototype; + builtins._internal->numberPrototype = d()->numberPrototype; + builtins._internal->datePrototype = d()->datePrototype; + builtins._internal->regExpPrototype = d()->regExpPrototype; + builtins._internal->errorPrototype = d()->errorPrototype; + builtins._internal->evalErrorPrototype = d()->evalErrorPrototype; + builtins._internal->rangeErrorPrototype = d()->rangeErrorPrototype; + builtins._internal->referenceErrorPrototype = d()->referenceErrorPrototype; + builtins._internal->syntaxErrorPrototype = d()->syntaxErrorPrototype; + builtins._internal->typeErrorPrototype = d()->typeErrorPrototype; + builtins._internal->URIErrorPrototype = d()->URIErrorPrototype; +} + +void JSGlobalObject::restoreBuiltins(const SavedBuiltins& builtins) +{ + if (!builtins._internal) + return; + + d()->objectConstructor = builtins._internal->objectConstructor; + d()->functionConstructor = builtins._internal->functionConstructor; + d()->arrayConstructor = builtins._internal->arrayConstructor; + d()->booleanConstructor = builtins._internal->booleanConstructor; + d()->stringConstructor = builtins._internal->stringConstructor; + d()->numberConstructor = builtins._internal->numberConstructor; + d()->dateConstructor = builtins._internal->dateConstructor; + d()->regExpConstructor = builtins._internal->regExpConstructor; + d()->errorConstructor = builtins._internal->errorConstructor; + d()->evalErrorConstructor = builtins._internal->evalErrorConstructor; + d()->rangeErrorConstructor = builtins._internal->rangeErrorConstructor; + d()->referenceErrorConstructor = builtins._internal->referenceErrorConstructor; + d()->syntaxErrorConstructor = builtins._internal->syntaxErrorConstructor; + d()->typeErrorConstructor = builtins._internal->typeErrorConstructor; + d()->URIErrorConstructor = builtins._internal->URIErrorConstructor; + + d()->objectPrototype = builtins._internal->objectPrototype; + d()->functionPrototype = builtins._internal->functionPrototype; + d()->arrayPrototype = builtins._internal->arrayPrototype; + d()->booleanPrototype = builtins._internal->booleanPrototype; + d()->stringPrototype = builtins._internal->stringPrototype; + d()->numberPrototype = builtins._internal->numberPrototype; + d()->datePrototype = builtins._internal->datePrototype; + d()->regExpPrototype = builtins._internal->regExpPrototype; + d()->errorPrototype = builtins._internal->errorPrototype; + d()->evalErrorPrototype = builtins._internal->evalErrorPrototype; + d()->rangeErrorPrototype = builtins._internal->rangeErrorPrototype; + d()->referenceErrorPrototype = builtins._internal->referenceErrorPrototype; + d()->syntaxErrorPrototype = builtins._internal->syntaxErrorPrototype; + d()->typeErrorPrototype = builtins._internal->typeErrorPrototype; + d()->URIErrorPrototype = builtins._internal->URIErrorPrototype; +} + +void JSGlobalObject::mark() +{ + JSVariableObject::mark(); + + markIfNeeded(d()->globalExec.exception()); + + markIfNeeded(d()->objectConstructor); + markIfNeeded(d()->functionConstructor); + markIfNeeded(d()->arrayConstructor); + markIfNeeded(d()->booleanConstructor); + markIfNeeded(d()->stringConstructor); + markIfNeeded(d()->numberConstructor); + markIfNeeded(d()->dateConstructor); + markIfNeeded(d()->regExpConstructor); + markIfNeeded(d()->errorConstructor); + markIfNeeded(d()->evalErrorConstructor); + markIfNeeded(d()->rangeErrorConstructor); + markIfNeeded(d()->referenceErrorConstructor); + markIfNeeded(d()->syntaxErrorConstructor); + markIfNeeded(d()->typeErrorConstructor); + markIfNeeded(d()->URIErrorConstructor); + + markIfNeeded(d()->objectPrototype); + markIfNeeded(d()->functionPrototype); + markIfNeeded(d()->arrayPrototype); + markIfNeeded(d()->booleanPrototype); + markIfNeeded(d()->stringPrototype); + markIfNeeded(d()->numberPrototype); + markIfNeeded(d()->datePrototype); + markIfNeeded(d()->regExpPrototype); + markIfNeeded(d()->errorPrototype); + markIfNeeded(d()->evalErrorPrototype); + markIfNeeded(d()->rangeErrorPrototype); + markIfNeeded(d()->referenceErrorPrototype); + markIfNeeded(d()->syntaxErrorPrototype); + markIfNeeded(d()->typeErrorPrototype); + markIfNeeded(d()->URIErrorPrototype); +} + +ExecState* JSGlobalObject::globalExec() +{ + return &d()->globalExec; +} + +ActivationImp* JSGlobalObject::pushActivation(ExecState* exec) +{ + if (d()->activationCount == activationStackNodeSize) { + ActivationStackNode* newNode = new ActivationStackNode; + newNode->prev = d()->activations; + d()->activations = newNode; + d()->activationCount = 0; + } + + StackActivation* stackEntry = &d()->activations->data[d()->activationCount++]; + stackEntry->activationStorage.init(exec); + return &stackEntry->activationStorage; +} + +inline void JSGlobalObject::checkActivationCount() +{ + if (!d()->activationCount) { + ActivationStackNode* prev = d()->activations->prev; + ASSERT(prev); + delete d()->activations; + d()->activations = prev; + d()->activationCount = activationStackNodeSize; + } +} + +void JSGlobalObject::popActivation() +{ + checkActivationCount(); + d()->activations->data[--d()->activationCount].activationDataStorage.localStorage.shrink(0); +} + +void JSGlobalObject::tearOffActivation(ExecState* exec, bool leaveRelic) +{ + ActivationImp* oldActivation = exec->activationObject(); + if (!oldActivation || !oldActivation->isOnStack()) + return; + + ASSERT(exec->codeType() == FunctionCode); + ActivationImp* newActivation = new ActivationImp(*oldActivation->d(), leaveRelic); + + if (!leaveRelic) { + checkActivationCount(); + d()->activationCount--; + } + + oldActivation->d()->localStorage.shrink(0); + + exec->setActivationObject(newActivation); + exec->setVariableObject(newActivation); + exec->setLocalStorage(&newActivation->localStorage()); + exec->replaceScopeChainTop(newActivation); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/JSGlobalObject.h b/JavaScriptCore/kjs/JSGlobalObject.h new file mode 100644 index 0000000..d6c055e --- /dev/null +++ b/JavaScriptCore/kjs/JSGlobalObject.h @@ -0,0 +1,255 @@ +// -*- c-basic-offset: 4 -*- +/* + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU 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 KJS_GlobalObject_h +#define KJS_GlobalObject_h + +#include "JSVariableObject.h" + +namespace KJS { + + class ActivationImp; + class ArrayObjectImp; + class ArrayPrototype; + class BooleanObjectImp; + class BooleanPrototype; + class DateObjectImp; + class DatePrototype; + class Debugger; + class ErrorObjectImp; + class ErrorPrototype; + class EvalError; + class EvalErrorPrototype; + class FunctionObjectImp; + class FunctionPrototype; + class JSGlobalObject; + class NativeErrorImp; + class NativeErrorPrototype; + class NumberObjectImp; + class NumberPrototype; + class ObjectObjectImp; + class ObjectPrototype; + class RangeError; + class RangeErrorPrototype; + class ReferenceError; + class ReferenceError; + class ReferenceErrorPrototype; + class RegExpObjectImp; + class RegExpPrototype; + class RuntimeMethod; + class SavedBuiltins; + class ScopeChain; + class StringObjectImp; + class StringPrototype; + class SyntaxErrorPrototype; + class TypeError; + class TypeErrorPrototype; + class UriError; + class UriErrorPrototype; + struct ActivationStackNode; + + class JSGlobalObject : public JSVariableObject { + protected: + using JSVariableObject::JSVariableObjectData; + + struct JSGlobalObjectData : public JSVariableObjectData { + JSGlobalObjectData(JSGlobalObject* globalObject) + : JSVariableObjectData(&inlineSymbolTable) + , globalExec(globalObject) + { + } + + JSGlobalObject* next; + JSGlobalObject* prev; + + Debugger* debugger; + + GlobalExecState globalExec; + int recursion; + + unsigned timeoutTime; + unsigned timeAtLastCheckTimeout; + unsigned timeExecuting; + unsigned timeoutCheckCount; + unsigned tickCount; + unsigned ticksUntilNextTimeoutCheck; + + ObjectObjectImp* objectConstructor; + FunctionObjectImp* functionConstructor; + ArrayObjectImp* arrayConstructor; + BooleanObjectImp* booleanConstructor; + StringObjectImp* stringConstructor; + NumberObjectImp* numberConstructor; + DateObjectImp* dateConstructor; + RegExpObjectImp* regExpConstructor; + ErrorObjectImp* errorConstructor; + NativeErrorImp* evalErrorConstructor; + NativeErrorImp* rangeErrorConstructor; + NativeErrorImp* referenceErrorConstructor; + NativeErrorImp* syntaxErrorConstructor; + NativeErrorImp* typeErrorConstructor; + NativeErrorImp* URIErrorConstructor; + + ObjectPrototype* objectPrototype; + FunctionPrototype* functionPrototype; + ArrayPrototype* arrayPrototype; + BooleanPrototype* booleanPrototype; + StringPrototype* stringPrototype; + NumberPrototype* numberPrototype; + DatePrototype* datePrototype; + RegExpPrototype* regExpPrototype; + ErrorPrototype* errorPrototype; + NativeErrorPrototype* evalErrorPrototype; + NativeErrorPrototype* rangeErrorPrototype; + NativeErrorPrototype* referenceErrorPrototype; + NativeErrorPrototype* syntaxErrorPrototype; + NativeErrorPrototype* typeErrorPrototype; + NativeErrorPrototype* URIErrorPrototype; + + SymbolTable inlineSymbolTable; + + ActivationStackNode* activations; + size_t activationCount; + }; + + public: + JSGlobalObject() + : JSVariableObject(new JSGlobalObjectData(this)) + { + init(); + } + + protected: + JSGlobalObject(JSValue* proto) + : JSVariableObject(proto, new JSGlobalObjectData(this)) + { + init(); + } + + public: + virtual ~JSGlobalObject(); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void put(ExecState*, const Identifier&, JSValue*); + virtual void initializeVariable(ExecState*, const Identifier&, JSValue*, unsigned attributes); + + // Linked list of all global objects. + static JSGlobalObject* head() { return s_head; } + JSGlobalObject* next() { return d()->next; } + + // Resets the global object to contain only built-in properties, sets + // the global object's prototype to "prototype," then adds the + // default object prototype to the tail of the global object's + // prototype chain. + void reset(JSValue* prototype); + + // The following accessors return pristine values, even if a script + // replaces the global object's associated property. + + ObjectObjectImp* objectConstructor() const { return d()->objectConstructor; } + FunctionObjectImp* functionConstructor() const { return d()->functionConstructor; } + ArrayObjectImp* arrayConstructor() const { return d()->arrayConstructor; } + BooleanObjectImp* booleanConstructor() const { return d()->booleanConstructor; } + StringObjectImp* stringConstructor() const{ return d()->stringConstructor; } + NumberObjectImp* numberConstructor() const{ return d()->numberConstructor; } + DateObjectImp* dateConstructor() const{ return d()->dateConstructor; } + RegExpObjectImp* regExpConstructor() const { return d()->regExpConstructor; } + ErrorObjectImp* errorConstructor() const { return d()->errorConstructor; } + NativeErrorImp* evalErrorConstructor() const { return d()->evalErrorConstructor; } + NativeErrorImp* rangeErrorConstructor() const { return d()->rangeErrorConstructor; } + NativeErrorImp* referenceErrorConstructor() const { return d()->referenceErrorConstructor; } + NativeErrorImp* syntaxErrorConstructor() const { return d()->syntaxErrorConstructor; } + NativeErrorImp* typeErrorConstructor() const { return d()->typeErrorConstructor; } + NativeErrorImp* URIErrorConstructor() const { return d()->URIErrorConstructor; } + + ObjectPrototype* objectPrototype() const { return d()->objectPrototype; } + FunctionPrototype* functionPrototype() const { return d()->functionPrototype; } + ArrayPrototype* arrayPrototype() const { return d()->arrayPrototype; } + BooleanPrototype* booleanPrototype() const { return d()->booleanPrototype; } + StringPrototype* stringPrototype() const { return d()->stringPrototype; } + NumberPrototype* numberPrototype() const { return d()->numberPrototype; } + DatePrototype* datePrototype() const { return d()->datePrototype; } + RegExpPrototype* regExpPrototype() const { return d()->regExpPrototype; } + ErrorPrototype* errorPrototype() const { return d()->errorPrototype; } + NativeErrorPrototype* evalErrorPrototype() const { return d()->evalErrorPrototype; } + NativeErrorPrototype* rangeErrorPrototype() const { return d()->rangeErrorPrototype; } + NativeErrorPrototype* referenceErrorPrototype() const { return d()->referenceErrorPrototype; } + NativeErrorPrototype* syntaxErrorPrototype() const { return d()->syntaxErrorPrototype; } + NativeErrorPrototype* typeErrorPrototype() const { return d()->typeErrorPrototype; } + NativeErrorPrototype* URIErrorPrototype() const { return d()->URIErrorPrototype; } + + void saveBuiltins(SavedBuiltins&) const; + void restoreBuiltins(const SavedBuiltins&); + + void setTimeoutTime(unsigned timeoutTime) { d()->timeoutTime = timeoutTime; } + void startTimeoutCheck(); + void stopTimeoutCheck(); + bool timedOut(); + + Debugger* debugger() const { return d()->debugger; } + void setDebugger(Debugger* debugger) { d()->debugger = debugger; } + + int recursion() { return d()->recursion; } + void incRecursion() { ++d()->recursion; } + void decRecursion() { --d()->recursion; } + + virtual void mark(); + + virtual bool isGlobalObject() const { return true; } + + virtual ExecState* globalExec(); + + virtual bool shouldInterruptScript() const { return true; } + + virtual bool allowsAccessFrom(const JSGlobalObject*) const { return true; } + + ActivationImp* pushActivation(ExecState*); + void popActivation(); + void tearOffActivation(ExecState*, bool markAsRelic = false); + + private: + void init(); + + JSGlobalObjectData* d() const { return static_cast<JSGlobalObjectData*>(JSVariableObject::d); } + + bool checkTimeout(); + void resetTimeoutCheck(); + + void deleteActivationStack(); + void checkActivationCount(); + + static JSGlobalObject* s_head; + }; + + inline bool JSGlobalObject::timedOut() + { + d()->tickCount++; + + if (d()->tickCount != d()->ticksUntilNextTimeoutCheck) + return false; + + return checkTimeout(); + } + +} // namespace KJS + +#endif // KJS_GlobalObject_h diff --git a/JavaScriptCore/kjs/JSImmediate.cpp b/JavaScriptCore/kjs/JSImmediate.cpp new file mode 100644 index 0000000..ded8ccb --- /dev/null +++ b/JavaScriptCore/kjs/JSImmediate.cpp @@ -0,0 +1,82 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2003-2006 Apple Computer, 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 "JSImmediate.h" + +#include "JSGlobalObject.h" +#include "bool_object.h" +#include "number_object.h" +#include "object.h" + +namespace KJS { + +JSObject *JSImmediate::toObject(const JSValue *v, ExecState *exec) +{ + ASSERT(isImmediate(v)); + if (v == jsNull()) + return throwError(exec, TypeError, "Null value"); + else if (v == jsUndefined()) + return throwError(exec, TypeError, "Undefined value"); + else if (isBoolean(v)) { + List args; + args.append(const_cast<JSValue *>(v)); + return exec->lexicalGlobalObject()->booleanConstructor()->construct(exec, args); + } else { + ASSERT(isNumber(v)); + List args; + args.append(const_cast<JSValue *>(v)); + return exec->lexicalGlobalObject()->numberConstructor()->construct(exec, args); + } +} + +UString JSImmediate::toString(const JSValue *v) +{ + ASSERT(isImmediate(v)); + + if (v == jsNull()) + return "null"; + else if (v == jsUndefined()) + return "undefined"; + else if (v == jsBoolean(true)) + return "true"; + else if (v == jsBoolean(false)) + return "false"; + else { + ASSERT(isNumber(v)); + double d = toDouble(v); + if (d == 0.0) // +0.0 or -0.0 + return "0"; + return UString::from(d); + } +} + +JSType JSImmediate::type(const JSValue *v) +{ + ASSERT(isImmediate(v)); + + uintptr_t tag = getTag(v); + if (tag == UndefinedType) + return v == jsUndefined() ? UndefinedType : NullType; + return static_cast<JSType>(tag); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/JSImmediate.h b/JavaScriptCore/kjs/JSImmediate.h new file mode 100644 index 0000000..bc0cb16 --- /dev/null +++ b/JavaScriptCore/kjs/JSImmediate.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef KJS_JS_IMMEDIATE_H +#define KJS_JS_IMMEDIATE_H + +#include "JSType.h" +#include <wtf/Assertions.h> +#include <wtf/AlwaysInline.h> +#include <wtf/MathExtras.h> +#include <limits> +#include <stdarg.h> +#include <stdint.h> +#include <stdlib.h> + +namespace KJS { + +class ExecState; +class JSObject; +class JSValue; +class UString; + +/* + * A JSValue* is either a pointer to a cell (a heap-allocated object) or an immediate (a type-tagged + * signed int masquerading as a pointer). The low two bits in a JSValue* are available + * for type tagging because allocator alignment guarantees they will be 00 in cell pointers. + * + * For example, on a 32 bit system: + * + * JSCell*: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 00 + * [ high 30 bits: pointer address ] [ low 2 bits -- always 0 ] + * + * JSImmediate: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX TT + * [ high 30 bits: signed int ] [ low 2 bits -- type tag ] + * + * The bit "payload" (the high 30 bits) is a 30 bit signed int for immediate numbers, a flag to distinguish true/false + * and undefined/null. + * + * Notice that the JSType value of NullType is 4, which requires 3 bits to encode. Since we only have 2 bits + * available for type tagging, we tag the null immediate with UndefinedType, and JSImmediate::type() has + * to sort them out. + */ + +class JSImmediate { +public: + static ALWAYS_INLINE bool isImmediate(const JSValue* v) + { + return getTag(v) != 0; + } + + static ALWAYS_INLINE bool isNumber(const JSValue* v) + { + return (getTag(v) == NumberType); + } + + static ALWAYS_INLINE bool isBoolean(const JSValue* v) + { + return (getTag(v) == BooleanType); + } + + // Since we have room for only 3 unique tags, null and undefined have to share. + static ALWAYS_INLINE bool isUndefinedOrNull(const JSValue* v) + { + return (getTag(v) == UndefinedType); + } + + static JSValue* from(char); + static JSValue* from(signed char); + static JSValue* from(unsigned char); + static JSValue* from(short); + static JSValue* from(unsigned short); + static JSValue* from(int); + static JSValue* from(unsigned); + static JSValue* from(long); + static JSValue* from(unsigned long); + static JSValue* from(long long); + static JSValue* from(unsigned long long); + static JSValue* from(double); + + static ALWAYS_INLINE bool areBothImmediateNumbers(const JSValue* v1, const JSValue* v2) + { + return (reinterpret_cast<uintptr_t>(v1) & reinterpret_cast<uintptr_t>(v2) & TagMask) == NumberType; + } + + static ALWAYS_INLINE JSValue* andImmediateNumbers(const JSValue* v1, const JSValue* v2) + { + ASSERT(areBothImmediateNumbers(v1, v2)); + return reinterpret_cast<JSValue*>(reinterpret_cast<uintptr_t>(v1) & reinterpret_cast<uintptr_t>(v2)); + } + + static double toDouble(const JSValue*); + static bool toBoolean(const JSValue*); + static JSObject* toObject(const JSValue*, ExecState*); + static UString toString(const JSValue*); + static JSType type(const JSValue*); + + static bool getUInt32(const JSValue*, uint32_t&); + static bool getTruncatedInt32(const JSValue*, int32_t&); + static bool getTruncatedUInt32(const JSValue*, uint32_t&); + + static int32_t getTruncatedInt32(const JSValue*); + + static JSValue* trueImmediate(); + static JSValue* falseImmediate(); + static JSValue* undefinedImmediate(); + static JSValue* nullImmediate(); + +private: + static const uintptr_t TagMask = 3; // type tags are 2 bits long + + // Immediate values are restricted to a 30 bit signed value. + static const int minImmediateInt = -(1 << 29); + static const int maxImmediateInt = (1 << 29) - 1; + static const unsigned maxImmediateUInt = maxImmediateInt; + + static ALWAYS_INLINE JSValue* tag(uintptr_t bits, uintptr_t tag) + { + return reinterpret_cast<JSValue*>(bits | tag); + } + + static ALWAYS_INLINE uintptr_t unTag(const JSValue* v) + { + return reinterpret_cast<uintptr_t>(v) & ~TagMask; + } + + static ALWAYS_INLINE uintptr_t getTag(const JSValue* v) + { + return reinterpret_cast<uintptr_t>(v) & TagMask; + } +}; + +ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(1 << 2, BooleanType); } +ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(0, BooleanType); } +ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(1 << 2, UndefinedType); } +ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(0, UndefinedType); } + +ALWAYS_INLINE bool JSImmediate::toBoolean(const JSValue* v) +{ + ASSERT(isImmediate(v)); + uintptr_t bits = unTag(v); + return (bits != 0) & (JSImmediate::getTag(v) != UndefinedType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(char i) +{ + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(signed char i) +{ + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(unsigned char i) +{ + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(short i) +{ + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(unsigned short i) +{ + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(int i) +{ + if ((i < minImmediateInt) | (i > maxImmediateInt)) + return 0; + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(unsigned i) +{ + if (i > maxImmediateUInt) + return 0; + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(long i) +{ + if ((i < minImmediateInt) | (i > maxImmediateInt)) + return 0; + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(unsigned long i) +{ + if (i > maxImmediateUInt) + return 0; + return tag(i << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(long long i) +{ + if ((i < minImmediateInt) | (i > maxImmediateInt)) + return 0; + return tag(static_cast<uintptr_t>(i) << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(unsigned long long i) +{ + if (i > maxImmediateUInt) + return 0; + return tag(static_cast<uintptr_t>(i) << 2, NumberType); +} + +ALWAYS_INLINE JSValue* JSImmediate::from(double d) +{ + const int intVal = static_cast<int>(d); + + if ((intVal < minImmediateInt) | (intVal > maxImmediateInt)) + return 0; + + // Check for data loss from conversion to int. + if ((intVal != d) || (!intVal && signbit(d))) + return 0; + + return tag(intVal << 2, NumberType); +} + +ALWAYS_INLINE int32_t JSImmediate::getTruncatedInt32(const JSValue* v) +{ + ASSERT(isNumber(v)); + return static_cast<int32_t>(unTag(v)) >> 2; +} + +ALWAYS_INLINE double JSImmediate::toDouble(const JSValue* v) +{ + ASSERT(isImmediate(v)); + const int32_t i = static_cast<int32_t>(unTag(v)) >> 2; + if (JSImmediate::getTag(v) == UndefinedType && i) + return std::numeric_limits<double>::quiet_NaN(); + return i; +} + +ALWAYS_INLINE bool JSImmediate::getUInt32(const JSValue* v, uint32_t& i) +{ + const int32_t si = static_cast<int32_t>(unTag(v)) >> 2; + i = si; + return isNumber(v) & (si >= 0); +} + +ALWAYS_INLINE bool JSImmediate::getTruncatedInt32(const JSValue* v, int32_t& i) +{ + i = static_cast<int32_t>(unTag(v)) >> 2; + return isNumber(v); +} + +ALWAYS_INLINE bool JSImmediate::getTruncatedUInt32(const JSValue* v, uint32_t& i) +{ + return getUInt32(v, i); +} + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/kjs/JSLock.cpp b/JavaScriptCore/kjs/JSLock.cpp new file mode 100644 index 0000000..9d51ca9 --- /dev/null +++ b/JavaScriptCore/kjs/JSLock.cpp @@ -0,0 +1,143 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2005 Apple Computer, 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 "JSLock.h" + +#include "collector.h" +#if USE(MULTIPLE_THREADS) +#include <pthread.h> +#endif + +namespace KJS { + +#if USE(MULTIPLE_THREADS) + +// Acquire this mutex before accessing lock-related data. +static pthread_mutex_t JSMutex = PTHREAD_MUTEX_INITIALIZER; + +// Thread-specific key that tells whether a thread holds the JSMutex. +pthread_key_t didLockJSMutex; + +// Lock nesting count. +static int JSLockCount; + +static void createDidLockJSMutex() +{ + pthread_key_create(&didLockJSMutex, 0); +} +pthread_once_t createDidLockJSMutexOnce = PTHREAD_ONCE_INIT; + +void JSLock::lock() +{ + pthread_once(&createDidLockJSMutexOnce, createDidLockJSMutex); + + if (!pthread_getspecific(didLockJSMutex)) { + int result; + result = pthread_mutex_lock(&JSMutex); + ASSERT(!result); + pthread_setspecific(didLockJSMutex, &didLockJSMutex); + } + ++JSLockCount; +} + +void JSLock::unlock() +{ + ASSERT(JSLockCount); + ASSERT(!!pthread_getspecific(didLockJSMutex)); + + --JSLockCount; + if (!JSLockCount) { + pthread_setspecific(didLockJSMutex, 0); + int result; + result = pthread_mutex_unlock(&JSMutex); + ASSERT(!result); + } +} + +bool JSLock::currentThreadIsHoldingLock() +{ + pthread_once(&createDidLockJSMutexOnce, createDidLockJSMutex); + return !!pthread_getspecific(didLockJSMutex); +} + +void JSLock::registerThread() +{ + Collector::registerThread(); +} + +JSLock::DropAllLocks::DropAllLocks() + : m_lockCount(0) +{ + pthread_once(&createDidLockJSMutexOnce, createDidLockJSMutex); + + m_lockCount = !!pthread_getspecific(didLockJSMutex) ? JSLock::lockCount() : 0; + for (int i = 0; i < m_lockCount; i++) + JSLock::unlock(); +} + +JSLock::DropAllLocks::~DropAllLocks() +{ + for (int i = 0; i < m_lockCount; i++) + JSLock::lock(); + m_lockCount = 0; +} + +#else + +// If threading support is off, set the lock count to a constant value of 1 so assertions +// that the lock is held don't fail +const int JSLockCount = 1; + +bool JSLock::currentThreadIsHoldingLock() +{ + return true; +} + +void JSLock::lock() +{ +} + +void JSLock::unlock() +{ +} + +void JSLock::registerThread() +{ +} + +JSLock::DropAllLocks::DropAllLocks() +{ +} + +JSLock::DropAllLocks::~DropAllLocks() +{ +} + +#endif // USE(MULTIPLE_THREADS) + +int JSLock::lockCount() +{ + return JSLockCount; +} + +} diff --git a/JavaScriptCore/kjs/JSLock.h b/JavaScriptCore/kjs/JSLock.h new file mode 100644 index 0000000..f6cda5d --- /dev/null +++ b/JavaScriptCore/kjs/JSLock.h @@ -0,0 +1,81 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2005 Apple Computer, 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. + * + */ + +#ifndef KJS_JSLock_h +#define KJS_JSLock_h + +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> + +namespace KJS { + + // 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. + + class JSLock : Noncopyable { + public: + JSLock() + { + lock(); + registerThread(); + } + + ~JSLock() + { + unlock(); + } + + static void lock(); + static void unlock(); + static int lockCount(); + static bool currentThreadIsHoldingLock(); + + static void registerThread(); + + class DropAllLocks : Noncopyable { + public: + DropAllLocks(); + ~DropAllLocks(); + + private: + int m_lockCount; + }; + }; + +} // namespace + +#endif // KJS_JSLock_h diff --git a/JavaScriptCore/kjs/JSType.h b/JavaScriptCore/kjs/JSType.h new file mode 100644 index 0000000..1c9418c --- /dev/null +++ b/JavaScriptCore/kjs/JSType.h @@ -0,0 +1,43 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2006 Apple Computer, 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. + * + */ + +#ifndef KJS_JSTYPE_H +#define KJS_JSTYPE_H + +namespace KJS { + +/** + * Primitive types + */ +enum JSType { + UnspecifiedType = 0, + UndefinedType = 1, + BooleanType = 2, + NumberType = 3, + NullType = 4, + StringType = 5, + ObjectType = 6, + GetterSetterType = 7 +}; + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/kjs/JSVariableObject.cpp b/JavaScriptCore/kjs/JSVariableObject.cpp new file mode 100644 index 0000000..76d563d --- /dev/null +++ b/JavaScriptCore/kjs/JSVariableObject.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSVariableObject.h" + +#include "PropertyNameArray.h" +#include "property_map.h" + +namespace KJS { + +UString::Rep* IdentifierRepHashTraits::nullRepPtr = &UString::Rep::null; // Didn't want to make a whole source file for just this. + +void JSVariableObject::saveLocalStorage(SavedProperties& p) const +{ + ASSERT(d->symbolTable); + ASSERT(static_cast<size_t>(d->symbolTable->size()) == d->localStorage.size()); + + unsigned count = d->symbolTable->size(); + + p.properties.clear(); + p.count = count; + + if (!count) + return; + + p.properties.set(new SavedProperty[count]); + + SymbolTable::const_iterator end = d->symbolTable->end(); + for (SymbolTable::const_iterator it = d->symbolTable->begin(); it != end; ++it) { + size_t i = it->second; + const LocalStorageEntry& entry = d->localStorage[i]; + p.properties[i].init(it->first.get(), entry.value, entry.attributes); + } +} + +void JSVariableObject::restoreLocalStorage(const SavedProperties& p) +{ + unsigned count = p.count; + d->symbolTable->clear(); + d->localStorage.resize(count); + SavedProperty* property = p.properties.get(); + for (size_t i = 0; i < count; ++i, ++property) { + ASSERT(!d->symbolTable->contains(property->name())); + LocalStorageEntry& entry = d->localStorage[i]; + d->symbolTable->set(property->name(), i); + entry.value = property->value(); + entry.attributes = property->attributes(); + } +} + +bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (symbolTable().contains(propertyName.ustring().rep())) + return false; + + return JSObject::deleteProperty(exec, propertyName); +} + +void JSVariableObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + SymbolTable::const_iterator::Keys end = symbolTable().end().keys(); + for (SymbolTable::const_iterator::Keys it = symbolTable().begin().keys(); it != end; ++it) + propertyNames.add(Identifier(it->get())); + + JSObject::getPropertyNames(exec, propertyNames); +} + +void JSVariableObject::mark() +{ + JSObject::mark(); + + size_t size = d->localStorage.size(); + for (size_t i = 0; i < size; ++i) { + JSValue* value = d->localStorage[i].value; + if (!value->marked()) + value->mark(); + } +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/JSVariableObject.h b/JavaScriptCore/kjs/JSVariableObject.h new file mode 100644 index 0000000..e22e9c6 --- /dev/null +++ b/JavaScriptCore/kjs/JSVariableObject.h @@ -0,0 +1,135 @@ +/* + * 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 "LocalStorage.h" +#include "SymbolTable.h" +#include "object.h" + +namespace KJS { + + class JSVariableObject : public JSObject { + public: + SymbolTable& symbolTable() { return *d->symbolTable; } + LocalStorage& localStorage() { return d->localStorage; } + + void saveLocalStorage(SavedProperties&) const; + void restoreLocalStorage(const SavedProperties&); + + virtual void initializeVariable(ExecState*, const Identifier&, JSValue*, unsigned attributes) = 0; + + virtual bool deleteProperty(ExecState*, const Identifier&); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual void mark(); + + protected: + // Subclasses of JSVariableObject can subclass this struct to add data + // without increasing their own size (since there's a hard limit on the + // size of a JSCell). + struct JSVariableObjectData { + JSVariableObjectData() { } + JSVariableObjectData(SymbolTable* s) + : symbolTable(s) // Subclass owns this pointer. + { + } + + LocalStorage localStorage; // Storage for variables in the symbol table. + SymbolTable* symbolTable; // Maps name -> index in localStorage. + }; + + JSVariableObject() { } + + JSVariableObject(JSVariableObjectData* data) + : d(data) // Subclass owns this pointer. + { + } + + JSVariableObject(JSValue* proto, JSVariableObjectData* data) + : JSObject(proto) + , d(data) // Subclass owns this pointer. + { + } + + bool symbolTableGet(const Identifier&, PropertySlot&); + bool symbolTablePut(const Identifier&, JSValue*); + bool symbolTableInitializeVariable(const Identifier&, JSValue*, unsigned attributes); + + JSVariableObjectData* d; + }; + + inline bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) + { + size_t index = symbolTable().get(propertyName.ustring().rep()); + if (index != missingSymbolMarker()) { +#ifndef NDEBUG + // During initialization, the variable object needs to advertise that it has certain + // properties, even if they're not ready for access yet. This check verifies that + // no one tries to access such a property. + + // In a release build, we optimize this check away and just return an invalid pointer. + // There's no harm in an invalid pointer, since no one dereferences it. + if (index >= d->localStorage.size()) { + slot.setUngettable(this); + return true; + } +#endif + slot.setValueSlot(this, &d->localStorage[index].value); + return true; + } + return false; + } + + inline bool JSVariableObject::symbolTablePut(const Identifier& propertyName, JSValue* value) + { + size_t index = symbolTable().get(propertyName.ustring().rep()); + if (index == missingSymbolMarker()) + return false; + LocalStorageEntry& entry = d->localStorage[index]; + if (entry.attributes & ReadOnly) + return true; + entry.value = value; + return true; + } + + inline bool JSVariableObject::symbolTableInitializeVariable(const Identifier& propertyName, JSValue* value, unsigned attributes) + { + size_t index = symbolTable().get(propertyName.ustring().rep()); + if (index == missingSymbolMarker()) + return false; + LocalStorageEntry& entry = d->localStorage[index]; + entry.value = value; + entry.attributes = attributes; + return true; + } + +} // namespace KJS + +#endif // JSVariableObject_h diff --git a/JavaScriptCore/kjs/JSWrapperObject.cpp b/JavaScriptCore/kjs/JSWrapperObject.cpp new file mode 100644 index 0000000..dd161f7 --- /dev/null +++ b/JavaScriptCore/kjs/JSWrapperObject.cpp @@ -0,0 +1,34 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 2006 Maks Orlovich + * Copyright (C) 2006 Apple Computer, 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 "JSWrapperObject.h" + +namespace KJS { + +void JSWrapperObject::mark() +{ + JSObject::mark(); + if (m_internalValue && !m_internalValue->marked()) + m_internalValue->mark(); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/JSWrapperObject.h b/JavaScriptCore/kjs/JSWrapperObject.h new file mode 100644 index 0000000..0a06c9f --- /dev/null +++ b/JavaScriptCore/kjs/JSWrapperObject.h @@ -0,0 +1,84 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2006 Maks Orlovich + * Copyright (C) 2006 Apple Computer, 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. + * + */ + +#ifndef KJS_JSWrapperObject_h +#define KJS_JSWrapperObject_h + +#include "object.h" + +namespace KJS { + + /** + This class is used as a base for classes such as String, + Number, Boolean and Date which which are wrappers for primitive + types. These classes stores the internal value, which is the + actual value represented by the wrapper objects. + */ + class JSWrapperObject : public JSObject { + public: + JSWrapperObject(JSValue* proto); + + /** + * Returns the internal value of the object. This is used for objects such + * as String and Boolean which are wrappers for native types. The interal + * value is the actual value represented by the wrapper objects. + * + * @see ECMA 8.6.2 + * @return The internal value of the object + */ + JSValue* internalValue() const; + + /** + * Sets the internal value of the object + * + * @see internalValue() + * + * @param v The new internal value + */ + void setInternalValue(JSValue* v); + + virtual void mark(); + + private: + JSValue* m_internalValue; + }; + + inline JSWrapperObject::JSWrapperObject(JSValue* proto) + : JSObject(proto) + , m_internalValue(0) + { + } + + inline JSValue* JSWrapperObject::internalValue() const + { + return m_internalValue; + } + + inline void JSWrapperObject::setInternalValue(JSValue* v) + { + ASSERT(v); + m_internalValue = v; + } + +} // namespace KJS + +#endif // KJS_JSWrapperObject_h diff --git a/JavaScriptCore/kjs/LabelStack.h b/JavaScriptCore/kjs/LabelStack.h new file mode 100644 index 0000000..375edf1 --- /dev/null +++ b/JavaScriptCore/kjs/LabelStack.h @@ -0,0 +1,84 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 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 KJS_LABEL_STACK_H +#define KJS_LABEL_STACK_H + +#include "identifier.h" +#include <wtf/Noncopyable.h> + +namespace KJS { + /** + * @short The "label set" in Ecma-262 spec + */ + class LabelStack : Noncopyable { + public: + LabelStack() + : tos(0) + { + } + ~LabelStack(); + + /** + * If id is not empty and is not in the stack already, puts it on top of + * the stack and returns true, otherwise returns false + */ + bool push(const Identifier &id); + /** + * Is the id in the stack? + */ + bool contains(const Identifier &id) const; + /** + * Removes from the stack the last pushed id (what else?) + */ + void pop(); + + private: + struct StackElem { + Identifier id; + StackElem *prev; + }; + + StackElem *tos; + }; + +inline LabelStack::~LabelStack() +{ + StackElem *prev; + for (StackElem *e = tos; e; e = prev) { + prev = e->prev; + delete e; + } +} + +inline void LabelStack::pop() +{ + if (StackElem *e = tos) { + tos = e->prev; + delete e; + } +} + +} + +#endif // KJS_LABEL_STACK_H diff --git a/JavaScriptCore/kjs/LocalStorage.h b/JavaScriptCore/kjs/LocalStorage.h new file mode 100644 index 0000000..87a54bb --- /dev/null +++ b/JavaScriptCore/kjs/LocalStorage.h @@ -0,0 +1,56 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * 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 KJS_LOCAL_STORAGE_H +#define KJS_LOCAL_STORAGE_H + +#include <wtf/Forward.h> +#include <wtf/VectorTraits.h> + +namespace KJS { + class JSValue; + + struct LocalStorageEntry { + LocalStorageEntry() + { + } + + LocalStorageEntry(JSValue* v, unsigned a) + : value(v) + , attributes(a) + { + } + + JSValue* value; + unsigned attributes; + }; + + typedef Vector<LocalStorageEntry, 32> LocalStorage; +} + +namespace WTF { + template<> struct VectorTraits<KJS::LocalStorageEntry> : VectorTraitsBase<true, KJS::LocalStorageEntry> { }; +} + +#endif // KJS_LOCAL_STORAGE_H diff --git a/JavaScriptCore/kjs/NodeInfo.h b/JavaScriptCore/kjs/NodeInfo.h new file mode 100644 index 0000000..60637f2 --- /dev/null +++ b/JavaScriptCore/kjs/NodeInfo.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NodeInfo_h +#define NodeInfo_h + +#include "nodes.h" +#include "Parser.h" + +namespace KJS { + +template <typename T> struct NodeInfo { + T m_node; + ParserRefCountedData<DeclarationStacks::VarStack>* m_varDeclarations; + ParserRefCountedData<DeclarationStacks::FunctionStack>* m_funcDeclarations; +}; + +typedef NodeInfo<StatementNode*> StatementNodeInfo; +typedef NodeInfo<CaseBlockNode*> CaseBlockNodeInfo; +typedef NodeInfo<CaseClauseNode*> CaseClauseNodeInfo; +typedef NodeInfo<SourceElements*> SourceElementsInfo; +typedef NodeInfo<ClauseList> ClauseListInfo; +typedef NodeInfo<ExpressionNode*> VarDeclListInfo; +typedef NodeInfo<ConstDeclList> ConstDeclListInfo; + +} // namespace KJS + +#endif // NodeInfo_h diff --git a/JavaScriptCore/kjs/Parser.cpp b/JavaScriptCore/kjs/Parser.cpp new file mode 100644 index 0000000..c6d7265 --- /dev/null +++ b/JavaScriptCore/kjs/Parser.cpp @@ -0,0 +1,91 @@ +// -*- c-basic-offset: 4 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2006, 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 "Parser.h" + +#include "lexer.h" +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +extern int kjsyyparse(); + +namespace KJS { + +Parser::Parser() + : m_sourceId(0) +{ +} + +void Parser::parse(int startingLineNumber, + const UChar* code, unsigned length, + int* sourceId, int* errLine, UString* errMsg) +{ + ASSERT(!m_sourceElements); + + if (errLine) + *errLine = -1; + if (errMsg) + *errMsg = 0; + + Lexer& lexer = KJS::lexer(); + + lexer.setCode(startingLineNumber, code, length); + m_sourceId++; + if (sourceId) + *sourceId = m_sourceId; + + int parseError = kjsyyparse(); + bool lexError = lexer.sawError(); + lexer.clear(); + + ParserRefCounted::deleteNewObjects(); + + if (parseError || lexError) { + if (errLine) + *errLine = lexer.lineNo(); + if (errMsg) + *errMsg = "Parse error"; + m_sourceElements.clear(); + } +} + +void Parser::didFinishParsing(SourceElements* sourceElements, ParserRefCountedData<DeclarationStacks::VarStack>* varStack, + ParserRefCountedData<DeclarationStacks::FunctionStack>* funcStack, int lastLine) +{ + m_sourceElements = sourceElements ? sourceElements : new SourceElements; + m_varDeclarations = varStack; + m_funcDeclarations = funcStack; + m_lastLine = lastLine; +} + +Parser& parser() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + static Parser& staticParser = *new Parser; + return staticParser; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/Parser.h b/JavaScriptCore/kjs/Parser.h new file mode 100644 index 0000000..92548fe --- /dev/null +++ b/JavaScriptCore/kjs/Parser.h @@ -0,0 +1,99 @@ +// -*- c-basic-offset: 4 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2006, 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. + * + */ + +#ifndef Parser_h +#define Parser_h + +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefPtr.h> +#include "nodes.h" + +namespace KJS { + + class FunctionBodyNode; + class ProgramNode; + class UString; + + struct UChar; + + template <typename T> struct ParserRefCountedData : ParserRefCounted { + T data; + }; + + class Parser : Noncopyable { + public: + template <class ParsedNode> + PassRefPtr<ParsedNode> parse(const UString& sourceURL, int startingLineNumber, + const UChar* code, unsigned length, + int* sourceId = 0, int* errLine = 0, UString* errMsg = 0); + + UString sourceURL() const { return m_sourceURL; } + int sourceId() const { return m_sourceId; } + + void didFinishParsing(SourceElements*, ParserRefCountedData<DeclarationStacks::VarStack>*, + ParserRefCountedData<DeclarationStacks::FunctionStack>*, int lastLine); + + private: + friend Parser& parser(); + + Parser(); // Use parser() instead. + void parse(int startingLineNumber, const UChar* code, unsigned length, + int* sourceId, int* errLine, UString* errMsg); + + UString m_sourceURL; + int m_sourceId; + RefPtr<SourceElements> m_sourceElements; + RefPtr<ParserRefCountedData<DeclarationStacks::VarStack> > m_varDeclarations; + RefPtr<ParserRefCountedData<DeclarationStacks::FunctionStack> > m_funcDeclarations; + int m_lastLine; + }; + + Parser& parser(); // Returns the singleton JavaScript parser. + + template <class ParsedNode> + PassRefPtr<ParsedNode> Parser::parse(const UString& sourceURL, int startingLineNumber, + const UChar* code, unsigned length, + int* sourceId, int* errLine, UString* errMsg) + { + m_sourceURL = sourceURL; + parse(startingLineNumber, code, length, sourceId, errLine, errMsg); + if (!m_sourceElements) { + m_sourceURL = UString(); + return 0; + } + RefPtr<ParsedNode> node = ParsedNode::create(m_sourceElements.release().get(), + m_varDeclarations ? &m_varDeclarations->data : 0, + m_funcDeclarations ? &m_funcDeclarations->data : 0); + m_varDeclarations = 0; + m_funcDeclarations = 0; + m_sourceURL = UString(); + node->setLoc(startingLineNumber, m_lastLine); + return node.release(); + } + +} // namespace KJS + +#endif // Parser_h diff --git a/JavaScriptCore/kjs/PropertyNameArray.cpp b/JavaScriptCore/kjs/PropertyNameArray.cpp new file mode 100644 index 0000000..45dda3a --- /dev/null +++ b/JavaScriptCore/kjs/PropertyNameArray.cpp @@ -0,0 +1,43 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2006 Apple Computer, 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 "PropertyNameArray.h" + +namespace KJS { + +void PropertyNameArray::add(const Identifier& ident) +{ + if (!m_set.add(ident.ustring().rep()).second) + return; + + m_vector.append(ident); +} + +void PropertyNameArray::swap(PropertyNameArray& other) +{ + m_vector.swap(other.m_vector); + m_set.swap(other.m_set); +} + + +} // namespace KJS + diff --git a/JavaScriptCore/kjs/PropertyNameArray.h b/JavaScriptCore/kjs/PropertyNameArray.h new file mode 100644 index 0000000..b702d21 --- /dev/null +++ b/JavaScriptCore/kjs/PropertyNameArray.h @@ -0,0 +1,56 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2006 Apple Computer, 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. + * + */ + +#ifndef KJS_PROPERTY_NAME_ARRAY_H +#define KJS_PROPERTY_NAME_ARRAY_H + +#include "identifier.h" + +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +namespace KJS { + + class PropertyNameArray { + public: + typedef Identifier ValueType; + typedef Vector<Identifier>::const_iterator const_iterator; + + void add(const Identifier&); + const_iterator begin() const { return m_vector.begin(); } + const_iterator end() const { return m_vector.end(); } + size_t size() const { return m_vector.size(); } + + Identifier& operator[](unsigned i) { return m_vector[i]; } + const Identifier& operator[](unsigned i) const { return m_vector[i]; } + + void swap(PropertyNameArray&); + private: + typedef HashSet<UString::Rep*, PtrHash<UString::Rep*> > IdentifierSet; + IdentifierSet m_set; + Vector<Identifier> m_vector; + }; + + +} // namespace KJS + + +#endif // KJS_PROPERTY_NAME_ARRAY_H diff --git a/JavaScriptCore/kjs/SavedBuiltins.h b/JavaScriptCore/kjs/SavedBuiltins.h new file mode 100644 index 0000000..9901e41 --- /dev/null +++ b/JavaScriptCore/kjs/SavedBuiltins.h @@ -0,0 +1,94 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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. + * + */ + +#ifndef SavedBuiltins_H +#define SavedBuiltins_H + +#include "protect.h" +#include "object_object.h" +#include "string_object.h" +#include "error_object.h" +#include "regexp_object.h" +#include "array_object.h" +#include "bool_object.h" +#include "date_object.h" +#include "number_object.h" +#include "math_object.h" + +namespace KJS { + +struct SavedBuiltinsInternal { + ProtectedPtr<ObjectObjectImp> objectConstructor; + ProtectedPtr<FunctionObjectImp> functionConstructor; + ProtectedPtr<ArrayObjectImp> arrayConstructor; + ProtectedPtr<BooleanObjectImp> booleanConstructor; + ProtectedPtr<StringObjectImp> stringConstructor; + ProtectedPtr<NumberObjectImp> numberConstructor; + ProtectedPtr<DateObjectImp> dateConstructor; + ProtectedPtr<RegExpObjectImp> regExpConstructor; + ProtectedPtr<ErrorObjectImp> errorConstructor; + ProtectedPtr<NativeErrorImp> evalErrorConstructor; + ProtectedPtr<NativeErrorImp> rangeErrorConstructor; + ProtectedPtr<NativeErrorImp> referenceErrorConstructor; + ProtectedPtr<NativeErrorImp> syntaxErrorConstructor; + ProtectedPtr<NativeErrorImp> typeErrorConstructor; + ProtectedPtr<NativeErrorImp> URIErrorConstructor; + + ProtectedPtr<ObjectPrototype> objectPrototype; + ProtectedPtr<FunctionPrototype> functionPrototype; + ProtectedPtr<ArrayPrototype> arrayPrototype; + ProtectedPtr<BooleanPrototype> booleanPrototype; + ProtectedPtr<StringPrototype> stringPrototype; + ProtectedPtr<NumberPrototype> numberPrototype; + ProtectedPtr<DatePrototype> datePrototype; + ProtectedPtr<RegExpPrototype> regExpPrototype; + ProtectedPtr<ErrorPrototype> errorPrototype; + ProtectedPtr<NativeErrorPrototype> evalErrorPrototype; + ProtectedPtr<NativeErrorPrototype> rangeErrorPrototype; + ProtectedPtr<NativeErrorPrototype> referenceErrorPrototype; + ProtectedPtr<NativeErrorPrototype> syntaxErrorPrototype; + ProtectedPtr<NativeErrorPrototype> typeErrorPrototype; + ProtectedPtr<NativeErrorPrototype> URIErrorPrototype; +}; + +class SavedBuiltins { + friend class JSGlobalObject; +public: + SavedBuiltins() + : _internal(0) + { + } + + ~SavedBuiltins() + { + delete _internal; + } + +private: + SavedBuiltinsInternal* _internal; +}; + +} // namespace + +#endif // SavedBuiltins_H diff --git a/JavaScriptCore/kjs/SymbolTable.h b/JavaScriptCore/kjs/SymbolTable.h new file mode 100644 index 0000000..1c2b2e8 --- /dev/null +++ b/JavaScriptCore/kjs/SymbolTable.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 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 "ustring.h" +#include <wtf/AlwaysInline.h> + +namespace KJS { + + struct IdentifierRepHash { + static unsigned hash(const RefPtr<UString::Rep>& key) { return key->computedHash(); } + static bool equal(const RefPtr<UString::Rep>& a, const RefPtr<UString::Rep>& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; + }; + + struct IdentifierRepHashTraits : HashTraits<RefPtr<UString::Rep> > { + static const RefPtr<UString::Rep>& deletedValue() + { + return *reinterpret_cast<RefPtr<UString::Rep>*>(&nullRepPtr); + } + + private: + static UString::Rep* nullRepPtr; + }; + + static ALWAYS_INLINE size_t missingSymbolMarker() { return std::numeric_limits<size_t>::max(); } + + struct SymbolTableIndexHashTraits { + typedef size_t TraitType; + typedef SymbolTableIndexHashTraits StorageTraits; + static size_t emptyValue() { return missingSymbolMarker(); } + static const bool emptyValueIsZero = false; + static const bool needsDestruction = false; + static const bool needsRef = false; + }; + + typedef HashMap<RefPtr<UString::Rep>, size_t, IdentifierRepHash, IdentifierRepHashTraits, SymbolTableIndexHashTraits> SymbolTable; + +} // namespace KJS + +#endif // SymbolTable_h diff --git a/JavaScriptCore/kjs/array_instance.cpp b/JavaScriptCore/kjs/array_instance.cpp new file mode 100644 index 0000000..1118f40 --- /dev/null +++ b/JavaScriptCore/kjs/array_instance.cpp @@ -0,0 +1,605 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "array_instance.h" + +#include "JSGlobalObject.h" +#include "PropertyNameArray.h" +#include <wtf/Assertions.h> + +using std::min; + +namespace KJS { + +typedef HashMap<unsigned, JSValue*> SparseArrayValueMap; + +struct ArrayStorage { + unsigned m_numValuesInVector; + SparseArrayValueMap* m_sparseValueMap; + JSValue* m_vector[1]; +}; + +// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer +static const unsigned maxArrayIndex = 0xFFFFFFFEU; + +// Our policy for when to use a vector and when to use a sparse map. +// For all array indices under sparseArrayCutoff, we always use a vector. +// When indices greater than sparseArrayCutoff 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 sparseArrayCutoff = 10000; +static const unsigned minDensityMultiplier = 8; + +static const unsigned copyingSortCutoff = 50000; + +const ClassInfo ArrayInstance::info = {"Array", 0, 0}; + +static inline size_t storageSize(unsigned vectorLength) +{ + return sizeof(ArrayStorage) - sizeof(JSValue*) + vectorLength * sizeof(JSValue*); +} + +static inline unsigned increasedVectorLength(unsigned newLength) +{ + return (newLength * 3 + 1) / 2; +} + +static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) +{ + return length / minDensityMultiplier <= numValues; +} + +ArrayInstance::ArrayInstance(JSObject* prototype, unsigned initialLength) + : JSObject(prototype) +{ + unsigned initialCapacity = min(initialLength, sparseArrayCutoff); + + m_length = initialLength; + m_vectorLength = initialCapacity; + m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); + + Collector::reportExtraMemoryCost(initialCapacity * sizeof(JSValue*)); +} + +ArrayInstance::ArrayInstance(JSObject* prototype, const List& list) + : JSObject(prototype) +{ + unsigned length = list.size(); + + m_length = length; + m_vectorLength = length; + + ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length))); + + storage->m_numValuesInVector = length; + storage->m_sparseValueMap = 0; + + size_t i = 0; + List::const_iterator end = list.end(); + for (List::const_iterator it = list.begin(); it != end; ++it, ++i) + storage->m_vector[i] = *it; + + m_storage = storage; + + // When the array is created non-empty, its cells are filled, so it's really no worse than + // a property map. Therefore don't report extra memory cost. +} + +ArrayInstance::~ArrayInstance() +{ + delete m_storage->m_sparseValueMap; + fastFree(m_storage); +} + +JSValue* ArrayInstance::getItem(unsigned i) const +{ + ASSERT(i <= maxArrayIndex); + + ArrayStorage* storage = m_storage; + + if (i < m_vectorLength) { + JSValue* value = storage->m_vector[i]; + return value ? value : jsUndefined(); + } + + SparseArrayValueMap* map = storage->m_sparseValueMap; + if (!map) + return jsUndefined(); + + JSValue* value = map->get(i); + return value ? value : jsUndefined(); +} + +JSValue* ArrayInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) +{ + return jsNumber(static_cast<ArrayInstance*>(slot.slotBase())->m_length); +} + +ALWAYS_INLINE bool ArrayInstance::inlineGetOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) +{ + ArrayStorage* storage = m_storage; + + if (i >= m_length) { + if (i > maxArrayIndex) + return getOwnPropertySlot(exec, Identifier::from(i), slot); + return false; + } + + if (i < m_vectorLength) { + JSValue*& valueSlot = storage->m_vector[i]; + if (valueSlot) { + slot.setValueSlot(this, &valueSlot); + return true; + } + } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (i >= sparseArrayCutoff) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->end()) { + slot.setValueSlot(this, &it->second); + return true; + } + } + } + + return false; +} + +bool ArrayInstance::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(&isArrayIndex); + if (isArrayIndex) + return inlineGetOwnPropertySlot(exec, i, slot); + + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool ArrayInstance::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) +{ + return inlineGetOwnPropertySlot(exec, i, slot); +} + +// ECMA 15.4.5.1 +void ArrayInstance::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(&isArrayIndex); + if (isArrayIndex) { + put(exec, i, value); + return; + } + + if (propertyName == exec->propertyNames().length) { + unsigned newLength = value->toUInt32(exec); + if (value->toNumber(exec) != static_cast<double>(newLength)) { + throwError(exec, RangeError, "Invalid array length."); + return; + } + setLength(newLength); + return; + } + + JSObject::put(exec, propertyName, value); +} + +void ArrayInstance::put(ExecState* exec, unsigned i, JSValue* value) +{ + if (i > maxArrayIndex) { + put(exec, Identifier::from(i), value); + return; + } + + ArrayStorage* storage = m_storage; + + unsigned length = m_length; + if (i >= length) { + length = i + 1; + m_length = length; + } + + if (i < m_vectorLength) { + JSValue*& valueSlot = storage->m_vector[i]; + storage->m_numValuesInVector += !valueSlot; + valueSlot = value; + return; + } + + if (i < sparseArrayCutoff) { + increaseVectorLength(i + 1); + storage = m_storage; + ++storage->m_numValuesInVector; + storage->m_vector[i] = value; + return; + } + + SparseArrayValueMap* map = storage->m_sparseValueMap; + if (!map || map->isEmpty()) { + if (isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) { + increaseVectorLength(i + 1); + storage = m_storage; + ++storage->m_numValuesInVector; + storage->m_vector[i] = value; + return; + } + if (!map) { + map = new SparseArrayValueMap; + storage->m_sparseValueMap = map; + } + map->set(i, value); + return; + } + + unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; + if (!isDenseEnoughForVector(i + 1, newNumValuesInVector)) { + map->set(i, value); + return; + } + + unsigned newVectorLength = increasedVectorLength(i + 1); + for (unsigned j = m_vectorLength; j < newVectorLength; ++j) + newNumValuesInVector += map->contains(j); + newNumValuesInVector -= map->contains(i); + if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { + unsigned proposedNewNumValuesInVector = newNumValuesInVector; + while (true) { + unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1); + for (unsigned j = newVectorLength; j < proposedNewVectorLength; ++j) + proposedNewNumValuesInVector += map->contains(j); + if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) + break; + newVectorLength = proposedNewVectorLength; + newNumValuesInVector = proposedNewNumValuesInVector; + } + } + + storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength))); + + unsigned vectorLength = m_vectorLength; + if (newNumValuesInVector == storage->m_numValuesInVector + 1) { + for (unsigned j = vectorLength; j < newVectorLength; ++j) + storage->m_vector[j] = 0; + map->remove(i); + } else { + for (unsigned j = vectorLength; j < newVectorLength; ++j) + storage->m_vector[j] = map->take(j); + } + + storage->m_vector[i] = value; + + m_vectorLength = newVectorLength; + storage->m_numValuesInVector = newNumValuesInVector; +} + +bool ArrayInstance::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + bool isArrayIndex; + unsigned i = propertyName.toArrayIndex(&isArrayIndex); + if (isArrayIndex) + return deleteProperty(exec, i); + + if (propertyName == exec->propertyNames().length) + return false; + + return JSObject::deleteProperty(exec, propertyName); +} + +bool ArrayInstance::deleteProperty(ExecState* exec, unsigned i) +{ + ArrayStorage* storage = m_storage; + + if (i < m_vectorLength) { + JSValue*& valueSlot = storage->m_vector[i]; + bool hadValue = valueSlot; + valueSlot = 0; + storage->m_numValuesInVector -= hadValue; + return hadValue; + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (i >= sparseArrayCutoff) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->end()) { + map->remove(it); + return true; + } + } + } + + if (i > maxArrayIndex) + return deleteProperty(exec, Identifier::from(i)); + + return false; +} + +void ArrayInstance::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + // FIXME: Filling PropertyNameArray with an identifier for every integer + // is incredibly inefficient for large arrays. We need a different approach. + + ArrayStorage* storage = m_storage; + + unsigned usedVectorLength = min(m_length, m_vectorLength); + for (unsigned i = 0; i < usedVectorLength; ++i) { + if (storage->m_vector[i]) + propertyNames.add(Identifier::from(i)); + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) + propertyNames.add(Identifier::from(it->first)); + } + + JSObject::getPropertyNames(exec, propertyNames); +} + +void ArrayInstance::increaseVectorLength(unsigned newLength) +{ + ArrayStorage* storage = m_storage; + + unsigned vectorLength = m_vectorLength; + ASSERT(newLength > vectorLength); + unsigned newVectorLength = increasedVectorLength(newLength); + + storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength))); + m_vectorLength = newVectorLength; + + for (unsigned i = vectorLength; i < newVectorLength; ++i) + storage->m_vector[i] = 0; + + m_storage = storage; +} + +void ArrayInstance::setLength(unsigned newLength) +{ + ArrayStorage* storage = m_storage; + + unsigned length = m_length; + + if (newLength < length) { + unsigned usedVectorLength = min(length, m_vectorLength); + for (unsigned i = newLength; i < usedVectorLength; ++i) { + JSValue*& valueSlot = storage->m_vector[i]; + bool hadValue = valueSlot; + valueSlot = 0; + storage->m_numValuesInVector -= hadValue; + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap copy = *map; + SparseArrayValueMap::iterator end = copy.end(); + for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) { + if (it->first >= newLength) + map->remove(it->first); + } + if (map->isEmpty()) { + delete map; + storage->m_sparseValueMap = 0; + } + } + } + + m_length = newLength; +} + +void ArrayInstance::mark() +{ + JSObject::mark(); + + ArrayStorage* storage = m_storage; + + unsigned usedVectorLength = min(m_length, m_vectorLength); + for (unsigned i = 0; i < usedVectorLength; ++i) { + JSValue* value = storage->m_vector[i]; + if (value && !value->marked()) + value->mark(); + } + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) { + JSValue* value = it->second; + if (!value->marked()) + value->mark(); + } + } +} + +static int compareByStringPairForQSort(const void* a, const void* b) +{ + const std::pair<JSValue*, UString>* va = static_cast<const std::pair<JSValue*, UString>*>(a); + const std::pair<JSValue*, UString>* vb = static_cast<const std::pair<JSValue*, UString>*>(b); + return compare(va->second, vb->second); +} + +static ExecState* execForCompareByStringForQSort = 0; +static int compareByStringForQSort(const void* a, const void* b) +{ + ExecState* exec = execForCompareByStringForQSort; + + JSValue* va = *static_cast<JSValue* const*>(a); + JSValue* vb = *static_cast<JSValue* const*>(b); + ASSERT(!va->isUndefined()); + ASSERT(!vb->isUndefined()); + + return compare(va->toString(exec), vb->toString(exec)); +} + +void ArrayInstance::sort(ExecState* exec) +{ + unsigned lengthNotIncludingUndefined = compactForSorting(); + + if (lengthNotIncludingUndefined < copyingSortCutoff) { + // 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. For large arrays, we fall back to a qsort on the JavaScriptValues to avoid creating copies. + + Vector<std::pair<JSValue*, UString> > values(lengthNotIncludingUndefined); + for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { + JSValue* value = m_storage->m_vector[i]; + ASSERT(!value->isUndefined()); + values[i].first = value; + values[i].second = value->toString(exec); + } + + // 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(std::pair<JSValue*, UString>), compareByStringPairForQSort); +#else + qsort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort); +#endif + for (size_t i = 0; i < lengthNotIncludingUndefined; i++) + m_storage->m_vector[i] = values[i].first; + return; + } + + ExecState* oldExec = execForCompareByStringForQSort; + execForCompareByStringForQSort = exec; + qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareByStringForQSort); + execForCompareByStringForQSort = oldExec; +} + +struct CompareWithCompareFunctionArguments { + CompareWithCompareFunctionArguments(ExecState *e, JSObject *cf) + : exec(e) + , compareFunction(cf) + , globalObject(e->dynamicGlobalObject()) + { + } + + ExecState *exec; + JSObject *compareFunction; + List arguments; + JSGlobalObject* globalObject; +}; + +static CompareWithCompareFunctionArguments* compareWithCompareFunctionArguments = 0; + +static int compareWithCompareFunctionForQSort(const void* a, const void* b) +{ + CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments; + + JSValue* va = *static_cast<JSValue* const*>(a); + JSValue* vb = *static_cast<JSValue* const*>(b); + ASSERT(!va->isUndefined()); + ASSERT(!vb->isUndefined()); + + args->arguments.clear(); + args->arguments.append(va); + args->arguments.append(vb); + double compareResult = args->compareFunction->call + (args->exec, args->globalObject, args->arguments)->toNumber(args->exec); + return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0; +} + +void ArrayInstance::sort(ExecState* exec, JSObject* compareFunction) +{ + size_t lengthNotIncludingUndefined = compactForSorting(); + + CompareWithCompareFunctionArguments* oldArgs = compareWithCompareFunctionArguments; + CompareWithCompareFunctionArguments args(exec, compareFunction); + compareWithCompareFunctionArguments = &args; + +#if HAVE(MERGESORT) + // Because mergesort usually does fewer compares, it is faster than qsort here. + // However, because it requires extra copies of the storage buffer, don't use it for very + // large arrays. + + // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even + // better job of minimizing compares. + + if (lengthNotIncludingUndefined < copyingSortCutoff) { + // During the sort, we could do a garbage collect, and it's important to still + // have references to every object in the array for ArrayInstance::mark. + // The mergesort algorithm does not guarantee this, so we sort a copy rather + // than the original. + size_t size = storageSize(m_vectorLength); + ArrayStorage* copy = static_cast<ArrayStorage*>(fastMalloc(size)); + memcpy(copy, m_storage, size); + mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort); + fastFree(m_storage); + m_storage = copy; + compareWithCompareFunctionArguments = oldArgs; + return; + } +#endif + + qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(JSValue*), compareWithCompareFunctionForQSort); + compareWithCompareFunctionArguments = oldArgs; +} + +unsigned ArrayInstance::compactForSorting() +{ + ArrayStorage* storage = m_storage; + + unsigned usedVectorLength = min(m_length, m_vectorLength); + + unsigned numDefined = 0; + unsigned numUndefined = 0; + + for (; numDefined < usedVectorLength; ++numDefined) { + JSValue* v = storage->m_vector[numDefined]; + if (!v || v->isUndefined()) + break; + } + for (unsigned i = numDefined; i < usedVectorLength; ++i) { + if (JSValue* v = storage->m_vector[i]) { + if (v->isUndefined()) + ++numUndefined; + else + storage->m_vector[numDefined++] = v; + } + } + + unsigned newUsedVectorLength = numDefined + numUndefined; + + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + newUsedVectorLength += map->size(); + if (newUsedVectorLength > m_vectorLength) { + increaseVectorLength(newUsedVectorLength); + storage = m_storage; + } + + SparseArrayValueMap::iterator end = map->end(); + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) + storage->m_vector[numDefined++] = it->second; + + delete map; + storage->m_sparseValueMap = 0; + } + + for (unsigned i = numDefined; i < newUsedVectorLength; ++i) + storage->m_vector[i] = jsUndefined(); + for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) + storage->m_vector[i] = 0; + + return numDefined; +} + +} diff --git a/JavaScriptCore/kjs/array_instance.h b/JavaScriptCore/kjs/array_instance.h new file mode 100644 index 0000000..d7efdde --- /dev/null +++ b/JavaScriptCore/kjs/array_instance.h @@ -0,0 +1,72 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * 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 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 ARRAY_INSTANCE_H +#define ARRAY_INSTANCE_H + +#include "object.h" + +namespace KJS { + + struct ArrayStorage; + + class ArrayInstance : public JSObject { + public: + ArrayInstance(JSObject* prototype, unsigned initialLength); + ArrayInstance(JSObject* prototype, const List& initialValues); + ~ArrayInstance(); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue*); + virtual void put(ExecState*, unsigned propertyName, JSValue*); + virtual bool deleteProperty(ExecState *, const Identifier& propertyName); + virtual bool deleteProperty(ExecState *, unsigned propertyName); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual void mark(); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + unsigned getLength() const { return m_length; } + JSValue* getItem(unsigned) const; + + void sort(ExecState*); + void sort(ExecState*, JSObject* compareFunction); + + private: + static JSValue* lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + bool inlineGetOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + + void setLength(unsigned); + void increaseVectorLength(unsigned newLength); + + unsigned compactForSorting(); + + unsigned m_length; + unsigned m_vectorLength; + ArrayStorage* m_storage; + }; + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/kjs/array_object.cpp b/JavaScriptCore/kjs/array_object.cpp new file mode 100644 index 0000000..48b0855 --- /dev/null +++ b/JavaScriptCore/kjs/array_object.cpp @@ -0,0 +1,760 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "array_object.h" +#include "array_object.lut.h" + +#include "error_object.h" +#include "lookup.h" +#include "operations.h" +#include <stdio.h> +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> + +#include <algorithm> // for std::min + +namespace KJS { + +// ------------------------------ ArrayPrototype ---------------------------- + +const ClassInfo ArrayPrototype::info = {"Array", &ArrayInstance::info, &arrayTable}; + +/* Source for array_object.lut.h +@begin arrayTable 16 + toString arrayProtoFuncToString DontEnum|Function 0 + toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0 + concat arrayProtoFuncConcat DontEnum|Function 1 + join arrayProtoFuncJoin DontEnum|Function 1 + pop arrayProtoFuncPop DontEnum|Function 0 + push arrayProtoFuncPush DontEnum|Function 1 + reverse arrayProtoFuncReverse DontEnum|Function 0 + shift arrayProtoFuncShift DontEnum|Function 0 + slice arrayProtoFuncSlice DontEnum|Function 2 + sort arrayProtoFuncSort DontEnum|Function 1 + splice arrayProtoFuncSplice DontEnum|Function 2 + unshift arrayProtoFuncUnShift DontEnum|Function 1 + every arrayProtoFuncEvery DontEnum|Function 1 + forEach arrayProtoFuncForEach DontEnum|Function 1 + some arrayProtoFuncSome DontEnum|Function 1 + indexOf arrayProtoFuncIndexOf DontEnum|Function 1 + lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1 + filter arrayProtoFuncFilter DontEnum|Function 1 + map arrayProtoFuncMap DontEnum|Function 1 +@end +*/ + +// ECMA 15.4.4 +ArrayPrototype::ArrayPrototype(ExecState*, ObjectPrototype* objProto) + : ArrayInstance(objProto, 0) +{ +} + +bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<ArrayInstance>(exec, &arrayTable, this, propertyName, slot); +} + + +// ------------------------------ Array Functions ---------------------------- + +// Helper function +static JSValue* getProperty(ExecState* exec, JSObject* obj, unsigned index) +{ + PropertySlot slot; + if (!obj->getPropertySlot(exec, index, slot)) + return 0; + return slot.getValue(exec, obj, index); +} + +JSValue* arrayProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&ArrayInstance::info)) + return throwError(exec, TypeError); + + static HashSet<JSObject*> visitedElems; + static const UString* empty = new UString(""); + static const UString* comma = new UString(","); + bool alreadyVisited = !visitedElems.add(thisObj).second; + if (alreadyVisited) + return jsString(*empty); + UString separator = *comma; + UString str = *empty; + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length; k++) { + if (k >= 1) + str += separator; + if (str.isNull()) { + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + break; + } + + JSValue* element = thisObj->get(exec, k); + if (element->isUndefinedOrNull()) + continue; + + str += element->toString(exec); + + if (str.isNull()) { + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + } + + if (exec->hadException()) + break; + } + visitedElems.remove(thisObj); + return jsString(str); +} + +JSValue* arrayProtoFuncToLocaleString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&ArrayInstance::info)) + return throwError(exec, TypeError); + + static HashSet<JSObject*> visitedElems; + static const UString* empty = new UString(""); + static const UString* comma = new UString(","); + bool alreadyVisited = !visitedElems.add(thisObj).second; + if (alreadyVisited) + return jsString(*empty); + UString separator = *comma; + UString str = *empty; + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length; k++) { + if (k >= 1) + str += separator; + if (str.isNull()) { + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + break; + } + + JSValue* element = thisObj->get(exec, k); + if (element->isUndefinedOrNull()) + continue; + + JSObject* o = element->toObject(exec); + JSValue* conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); + if (conversionFunction->isObject() && static_cast<JSObject*>(conversionFunction)->implementsCall()) + str += static_cast<JSObject*>(conversionFunction)->call(exec, o, exec->emptyList())->toString(exec); + else + str += element->toString(exec); + + if (str.isNull()) { + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + } + + if (exec->hadException()) + break; + } + visitedElems.remove(thisObj); + return jsString(str); +} + +JSValue* arrayProtoFuncJoin(ExecState* exec, JSObject* thisObj, const List& args) +{ + static HashSet<JSObject*> visitedElems; + static const UString* empty = new UString(""); + static const UString* comma = new UString(","); + bool alreadyVisited = !visitedElems.add(thisObj).second; + if (alreadyVisited) + return jsString(*empty); + UString separator = *comma; + UString str = *empty; + + if (!args[0]->isUndefined()) + separator = args[0]->toString(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length; k++) { + if (k >= 1) + str += separator; + if (str.isNull()) { + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + break; + } + + JSValue* element = thisObj->get(exec, k); + if (element->isUndefinedOrNull()) + continue; + + str += element->toString(exec); + + if (str.isNull()) { + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + } + + if (exec->hadException()) + break; + } + visitedElems.remove(thisObj); + return jsString(str); +} + +JSValue* arrayProtoFuncConcat(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* arr = static_cast<JSObject*>(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); + int n = 0; + JSValue* curArg = thisObj; + JSObject* curObj = static_cast<JSObject* >(thisObj); + List::const_iterator it = args.begin(); + List::const_iterator end = args.end(); + while (1) { + if (curArg->isObject() && curObj->inherits(&ArrayInstance::info)) { + unsigned k = 0; + // Older versions tried to optimize out getting the length of thisObj + // by checking for n != 0, but that doesn't work if thisObj is an empty array. + unsigned length = curObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + while (k < length) { + if (JSValue* v = getProperty(exec, curObj, k)) + arr->put(exec, n, v); + n++; + k++; + } + } else { + arr->put(exec, n, curArg); + n++; + } + if (it == end) + break; + curArg = *it; + curObj = static_cast<JSObject*>(curArg); // may be 0 + ++it; + } + arr->put(exec, exec->propertyNames().length, jsNumber(n)); + return arr; +} + +JSValue* arrayProtoFuncPop(ExecState* exec, JSObject* thisObj, const List&) +{ + JSValue* result = 0; + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + if (length == 0) { + thisObj->put(exec, exec->propertyNames().length, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, length - 1); + thisObj->deleteProperty(exec, length - 1); + thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1)); + } + return result; +} + +JSValue* arrayProtoFuncPush(ExecState* exec, JSObject* thisObj, const List& args) +{ + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned n = 0; n < args.size(); n++) + thisObj->put(exec, length + n, args[n]); + length += args.size(); + thisObj->put(exec, exec->propertyNames().length, jsNumber(length)); + return jsNumber(length); +} + +JSValue* arrayProtoFuncReverse(ExecState* exec, JSObject* thisObj, const List&) +{ + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + unsigned middle = length / 2; + + for (unsigned k = 0; k < middle; k++) { + unsigned lk1 = length - k - 1; + JSValue* obj2 = getProperty(exec, thisObj, lk1); + JSValue* obj = getProperty(exec, thisObj, k); + + if (obj2) + thisObj->put(exec, k, obj2); + else + thisObj->deleteProperty(exec, k); + + if (obj) + thisObj->put(exec, lk1, obj); + else + thisObj->deleteProperty(exec, lk1); + } + return thisObj; +} + +JSValue* arrayProtoFuncShift(ExecState* exec, JSObject* thisObj, const List&) +{ + JSValue* result = 0; + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + if (length == 0) { + thisObj->put(exec, exec->propertyNames().length, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, 0); + for (unsigned k = 1; k < length; k++) { + if (JSValue* obj = getProperty(exec, thisObj, k)) + thisObj->put(exec, k - 1, obj); + else + thisObj->deleteProperty(exec, k - 1); + } + thisObj->deleteProperty(exec, length - 1); + thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1)); + } + return result; +} + +JSValue* arrayProtoFuncSlice(ExecState* exec, JSObject* thisObj, const List& args) +{ + // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 + + // We return a new array + JSObject* resObj = static_cast<JSObject* >(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); + JSValue* result = resObj; + double begin = args[0]->toInteger(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + if (begin >= 0) { + if (begin > length) + begin = length; + } else { + begin += length; + if (begin < 0) + begin = 0; + } + double end; + if (args[1]->isUndefined()) + end = length; + else { + end = args[1]->toInteger(exec); + if (end < 0) { + end += length; + if (end < 0) + end = 0; + } else { + if (end > length) + end = length; + } + } + + int n = 0; + int b = static_cast<int>(begin); + int e = static_cast<int>(end); + for (int k = b; k < e; k++, n++) { + if (JSValue* v = getProperty(exec, thisObj, k)) + resObj->put(exec, n, v); + } + resObj->put(exec, exec->propertyNames().length, jsNumber(n)); + return result; +} + +JSValue* arrayProtoFuncSort(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* sortFunction = 0; + if (!args[0]->isUndefined()) { + sortFunction = args[0]->toObject(exec); + if (!sortFunction->implementsCall()) + sortFunction = 0; + } + + if (thisObj->classInfo() == &ArrayInstance::info) { + if (sortFunction) + static_cast<ArrayInstance*>(thisObj)->sort(exec, sortFunction); + else + static_cast<ArrayInstance*>(thisObj)->sort(exec); + return thisObj; + } + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + + if (!length) + return thisObj; + + // "Min" sort. Not the fastest, but definitely less code than heapsort + // or quicksort, and much less swapping than bubblesort/insertionsort. + for (unsigned i = 0; i < length - 1; ++i) { + JSValue* iObj = thisObj->get(exec, i); + unsigned themin = i; + JSValue* minObj = iObj; + for (unsigned j = i + 1; j < length; ++j) { + JSValue* jObj = thisObj->get(exec, j); + double compareResult; + if (jObj->isUndefined()) + compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) + else if (minObj->isUndefined()) + compareResult = -1; + else if (sortFunction) { + List l; + l.append(jObj); + l.append(minObj); + compareResult = sortFunction->call(exec, exec->dynamicGlobalObject(), l)->toNumber(exec); + } else + compareResult = (jObj->toString(exec) < minObj->toString(exec)) ? -1 : 1; + + if (compareResult < 0) { + themin = j; + minObj = jObj; + } + } + // Swap themin and i + if (themin > i) { + thisObj->put(exec, i, minObj); + thisObj->put(exec, themin, iObj); + } + } + return thisObj; +} + +JSValue* arrayProtoFuncSplice(ExecState* exec, JSObject* thisObj, const List& args) +{ + // 15.4.4.12 + JSObject* resObj = static_cast<JSObject* >(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); + JSValue* result = resObj; + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + if (!args.size()) + return jsUndefined(); + int begin = args[0]->toUInt32(exec); + if (begin < 0) + begin = std::max<int>(begin + length, 0); + else + begin = std::min<int>(begin, length); + + unsigned deleteCount; + if (args.size() > 1) + deleteCount = std::min<int>(std::max<int>(args[1]->toUInt32(exec), 0), length - begin); + else + deleteCount = length - begin; + + for (unsigned k = 0; k < deleteCount; k++) { + if (JSValue* v = getProperty(exec, thisObj, k + begin)) + resObj->put(exec, k, v); + } + resObj->put(exec, exec->propertyNames().length, jsNumber(deleteCount)); + + unsigned additionalArgs = std::max<int>(args.size() - 2, 0); + if (additionalArgs != deleteCount) { + if (additionalArgs < deleteCount) { + for (unsigned k = begin; k < length - deleteCount; ++k) { + if (JSValue* v = getProperty(exec, thisObj, k + deleteCount)) + thisObj->put(exec, k + additionalArgs, v); + else + thisObj->deleteProperty(exec, k + additionalArgs); + } + for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) + thisObj->deleteProperty(exec, k - 1); + } else { + for (unsigned k = length - deleteCount; (int)k > begin; --k) { + if (JSValue* obj = getProperty(exec, thisObj, k + deleteCount - 1)) + thisObj->put(exec, k + additionalArgs - 1, obj); + else + thisObj->deleteProperty(exec, k + additionalArgs - 1); + } + } + } + for (unsigned k = 0; k < additionalArgs; ++k) + thisObj->put(exec, k + begin, args[k + 2]); + + thisObj->put(exec, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); + return result; +} + +JSValue* arrayProtoFuncUnShift(ExecState* exec, JSObject* thisObj, const List& args) +{ + // 15.4.4.13 + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + unsigned nrArgs = args.size(); + if (nrArgs) { + for (unsigned k = length; k > 0; --k) { + if (JSValue* v = getProperty(exec, thisObj, k - 1)) + thisObj->put(exec, k + nrArgs - 1, v); + else + thisObj->deleteProperty(exec, k + nrArgs - 1); + } + } + for (unsigned k = 0; k < nrArgs; ++k) + thisObj->put(exec, k, args[k]); + JSValue* result = jsNumber(length + nrArgs); + thisObj->put(exec, exec->propertyNames().length, result); + return result; +} + +JSValue* arrayProtoFuncFilter(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* eachFunction = args[0]->toObject(exec); + + if (!eachFunction->implementsCall()) + return throwError(exec, TypeError); + + JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); + JSObject* resultArray = static_cast<JSObject*>(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); + + unsigned filterIndex = 0; + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length && !exec->hadException(); ++k) { + PropertySlot slot; + + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + JSValue* v = slot.getValue(exec, thisObj, k); + + List eachArguments; + + eachArguments.append(v); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + JSValue* result = eachFunction->call(exec, applyThis, eachArguments); + + if (result->toBoolean(exec)) + resultArray->put(exec, filterIndex++, v); + } + return resultArray; +} + +JSValue* arrayProtoFuncMap(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* eachFunction = args[0]->toObject(exec); + if (!eachFunction->implementsCall()) + return throwError(exec, TypeError); + + JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + + List mapArgs; + mapArgs.append(jsNumber(length)); + JSObject* resultArray = static_cast<JSObject*>(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, mapArgs)); + + for (unsigned k = 0; k < length && !exec->hadException(); ++k) { + PropertySlot slot; + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + JSValue* v = slot.getValue(exec, thisObj, k); + + List eachArguments; + + eachArguments.append(v); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + JSValue* result = eachFunction->call(exec, applyThis, eachArguments); + resultArray->put(exec, k, result); + } + + return 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 + +JSValue* arrayProtoFuncEvery(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* eachFunction = args[0]->toObject(exec); + + if (!eachFunction->implementsCall()) + return throwError(exec, TypeError); + + JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); + + JSValue* result = jsBoolean(true); + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length && !exec->hadException(); ++k) { + PropertySlot slot; + + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + List eachArguments; + + eachArguments.append(slot.getValue(exec, thisObj, k)); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec); + + if (!predicateResult) { + result = jsBoolean(false); + break; + } + } + + return result; +} + +JSValue* arrayProtoFuncForEach(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* eachFunction = args[0]->toObject(exec); + + if (!eachFunction->implementsCall()) + return throwError(exec, TypeError); + + JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length && !exec->hadException(); ++k) { + PropertySlot slot; + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + List eachArguments; + eachArguments.append(slot.getValue(exec, thisObj, k)); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + eachFunction->call(exec, applyThis, eachArguments); + } + return jsUndefined(); +} + +JSValue* arrayProtoFuncSome(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSObject* eachFunction = args[0]->toObject(exec); + + if (!eachFunction->implementsCall()) + return throwError(exec, TypeError); + + JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); + + JSValue* result = jsBoolean(false); + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned k = 0; k < length && !exec->hadException(); ++k) { + PropertySlot slot; + if (!thisObj->getPropertySlot(exec, k, slot)) + continue; + + List eachArguments; + eachArguments.append(slot.getValue(exec, thisObj, k)); + eachArguments.append(jsNumber(k)); + eachArguments.append(thisObj); + + bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec); + + if (predicateResult) { + result = jsBoolean(true); + break; + } + } + return result; +} + +JSValue* arrayProtoFuncIndexOf(ExecState* exec, JSObject* thisObj, const List& args) +{ + // JavaScript 1.5 Extension by Mozilla + // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf + + unsigned index = 0; + double d = args[1]->toInteger(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + if (d < 0) + d += length; + if (d > 0) { + if (d > length) + index = length; + else + index = static_cast<unsigned>(d); + } + + JSValue* searchElement = args[0]; + for (; index < length; ++index) { + JSValue* e = getProperty(exec, thisObj, index); + if (!e) + continue; + if (strictEqual(exec, searchElement, e)) + return jsNumber(index); + } + + return jsNumber(-1); +} + +JSValue* arrayProtoFuncLastIndexOf(ExecState* exec, JSObject* thisObj, const List& args) +{ + // JavaScript 1.6 Extension by Mozilla + // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf + + unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + int index = length - 1; + double d = args[1]->toIntegerPreserveNaN(exec); + + if (d < 0) { + d += length; + if (d < 0) + return jsNumber(-1); + } + if (d < length) + index = static_cast<int>(d); + + JSValue* searchElement = args[0]; + for (; index >= 0; --index) { + JSValue* e = getProperty(exec, thisObj, index); + if (!e) + continue; + if (strictEqual(exec, searchElement, e)) + return jsNumber(index); + } + + return jsNumber(-1); +} + +// ------------------------------ ArrayObjectImp ------------------------------- + +ArrayObjectImp::ArrayObjectImp(ExecState* exec, FunctionPrototype* funcProto, ArrayPrototype* arrayProto) + : InternalFunctionImp(funcProto, arrayProto->classInfo()->className) +{ + // ECMA 15.4.3.1 Array.prototype + putDirect(exec->propertyNames().prototype, arrayProto, DontEnum|DontDelete|ReadOnly); + + // no. of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); +} + +bool ArrayObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.4.2 +JSObject* ArrayObjectImp::construct(ExecState* exec, const List& args) +{ + // a single numeric argument denotes the array size (!) + if (args.size() == 1 && args[0]->isNumber()) { + uint32_t n = args[0]->toUInt32(exec); + if (n != args[0]->toNumber(exec)) + return throwError(exec, RangeError, "Array size is not a small enough positive integer."); + return new ArrayInstance(exec->lexicalGlobalObject()->arrayPrototype(), n); + } + + // otherwise the array is constructed with the arguments in it + return new ArrayInstance(exec->lexicalGlobalObject()->arrayPrototype(), args); +} + +// ECMA 15.6.1 +JSValue* ArrayObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + // equivalent to 'new Array(....)' + return construct(exec,args); +} + +} diff --git a/JavaScriptCore/kjs/array_object.h b/JavaScriptCore/kjs/array_object.h new file mode 100644 index 0000000..e109c47 --- /dev/null +++ b/JavaScriptCore/kjs/array_object.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ARRAY_OBJECT_H_ +#define ARRAY_OBJECT_H_ + +#include "array_instance.h" +#include "function_object.h" +#include "lookup.h" + +namespace KJS { + + class ArrayPrototype : public ArrayInstance { + public: + ArrayPrototype(ExecState*, ObjectPrototype*); + + bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + class ArrayObjectImp : public InternalFunctionImp { + public: + ArrayObjectImp(ExecState*, FunctionPrototype*, ArrayPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + + }; + + JSValue* arrayProtoFuncToString(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncToLocaleString(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncConcat(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncJoin(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncPop(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncPush(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncReverse(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncShift(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncSlice(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncSort(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncSplice(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncUnShift(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncEvery(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncForEach(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncSome(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncIndexOf(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncFilter(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncMap(ExecState*, JSObject*, const List&); + JSValue* arrayProtoFuncLastIndexOf(ExecState*, JSObject*, const List&); + +} // namespace KJS + +#endif // ARRAY_OBJECT_H_ diff --git a/JavaScriptCore/kjs/bool_object.cpp b/JavaScriptCore/kjs/bool_object.cpp new file mode 100644 index 0000000..10bb738 --- /dev/null +++ b/JavaScriptCore/kjs/bool_object.cpp @@ -0,0 +1,117 @@ +/* + * 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 "bool_object.h" + +#include "JSGlobalObject.h" +#include "error_object.h" +#include "operations.h" +#include <wtf/Assertions.h> + +namespace KJS { + +// ------------------------------ BooleanInstance --------------------------- + +const ClassInfo BooleanInstance::info = { "Boolean", 0, 0 }; + +BooleanInstance::BooleanInstance(JSObject* proto) + : JSWrapperObject(proto) +{ +} + +// ------------------------------ BooleanPrototype -------------------------- + +// Functions +static JSValue* booleanProtoFuncToString(ExecState*, JSObject*, const List&); +static JSValue* booleanProtoFuncValueOf(ExecState*, JSObject*, const List&); + +// ECMA 15.6.4 + +BooleanPrototype::BooleanPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype) + : BooleanInstance(objectPrototype) +{ + setInternalValue(jsBoolean(false)); + + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toString, booleanProtoFuncToString), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().valueOf, booleanProtoFuncValueOf), DontEnum); +} + + +// ------------------------------ Functions -------------------------- + +// ECMA 15.6.4.2 + 15.6.4.3 + +JSValue* booleanProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&BooleanInstance::info)) + return throwError(exec, TypeError); + + JSValue* v = static_cast<BooleanInstance*>(thisObj)->internalValue(); + ASSERT(v); + + return jsString(v->toString(exec)); +} +JSValue* booleanProtoFuncValueOf(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&BooleanInstance::info)) + return throwError(exec, TypeError); + + JSValue* v = static_cast<BooleanInstance*>(thisObj)->internalValue(); + ASSERT(v); + + // TODO: optimize for bool case + return jsBoolean(v->toBoolean(exec)); +} + +// ------------------------------ BooleanObjectImp ----------------------------- + + +BooleanObjectImp::BooleanObjectImp(ExecState* exec, FunctionPrototype* functionPrototype, BooleanPrototype* booleanPrototype) + : InternalFunctionImp(functionPrototype, booleanPrototype->classInfo()->className) +{ + putDirect(exec->propertyNames().prototype, booleanPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + + +bool BooleanObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.6.2 +JSObject* BooleanObjectImp::construct(ExecState* exec, const List& args) +{ + BooleanInstance* obj(new BooleanInstance(exec->lexicalGlobalObject()->booleanPrototype())); + obj->setInternalValue(jsBoolean(args[0]->toBoolean(exec))); + return obj; +} + +// ECMA 15.6.1 +JSValue* BooleanObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + // TODO: optimize for bool case + return jsBoolean(args[0]->toBoolean(exec)); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/bool_object.h b/JavaScriptCore/kjs/bool_object.h new file mode 100644 index 0000000..c3d5a9f --- /dev/null +++ b/JavaScriptCore/kjs/bool_object.h @@ -0,0 +1,65 @@ +/* + * 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 BOOL_OBJECT_H_ +#define BOOL_OBJECT_H_ + +#include "function_object.h" +#include "JSWrapperObject.h" + +namespace KJS { + + class BooleanInstance : public JSWrapperObject { + public: + BooleanInstance(JSObject* proto); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + /** + * @internal + * + * The initial value of Boolean.prototype (and thus all objects created + * with the Boolean constructor + */ + class BooleanPrototype : public BooleanInstance { + public: + BooleanPrototype(ExecState*, ObjectPrototype*, FunctionPrototype*); + }; + + /** + * @internal + * + * The initial value of the the global variable's "Boolean" property + */ + class BooleanObjectImp : public InternalFunctionImp { + public: + BooleanObjectImp(ExecState*, FunctionPrototype*, BooleanPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + }; + +} // namespace KJS + +#endif // BOOL_OBJECT_H_ diff --git a/JavaScriptCore/kjs/collector.cpp b/JavaScriptCore/kjs/collector.cpp new file mode 100644 index 0000000..23b93df --- /dev/null +++ b/JavaScriptCore/kjs/collector.cpp @@ -0,0 +1,1054 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "collector.h" + +#include "ExecState.h" +#include "JSGlobalObject.h" +#include "internal.h" +#include "list.h" +#include "value.h" +#include <algorithm> +#include <setjmp.h> +#include <stdlib.h> +#include <wtf/FastMalloc.h> +#include <wtf/HashCountedSet.h> +#include <wtf/UnusedParam.h> + +#if USE(MULTIPLE_THREADS) +#include <pthread.h> +#endif + +#if PLATFORM(DARWIN) + +#include <mach/mach_port.h> +#include <mach/mach_init.h> +#include <mach/task.h> +#include <mach/thread_act.h> +#include <mach/vm_map.h> + +#include "CollectorHeapIntrospector.h" + +#elif PLATFORM(WIN_OS) + +#include <windows.h> + +#elif PLATFORM(UNIX) + +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> + +#if PLATFORM(SOLARIS) +#include <thread.h> +#endif + +#if HAVE(PTHREAD_NP_H) +#include <pthread_np.h> +#else +#include <pthread.h> +#endif + +#endif + +#define DEBUG_COLLECTOR 0 + +using std::max; + +namespace KJS { + +// tunable parameters + +const size_t SPARE_EMPTY_BLOCKS = 2; +const size_t MIN_ARRAY_SIZE = 14; +const size_t GROWTH_FACTOR = 2; +const size_t LOW_WATER_FACTOR = 4; +const size_t ALLOCATIONS_PER_COLLECTION = 4000; + +static CollectorHeap primaryHeap = { 0, 0, 0, 0, 0, 0, 0, NoOperation }; +static CollectorHeap numberHeap = { 0, 0, 0, 0, 0, 0, 0, NoOperation }; + +// FIXME: I don't think this needs to be a static data member of the Collector class. +// Just a private global like "heap" above would be fine. +size_t Collector::mainThreadOnlyObjectCount = 0; + +static CollectorBlock* allocateBlock() +{ +#if PLATFORM(DARWIN) + vm_address_t address = 0; + vm_map(current_task(), &address, BLOCK_SIZE, BLOCK_OFFSET_MASK, VM_FLAGS_ANYWHERE, MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT); +#elif PLATFORM(WIN_OS) + // windows virtual address granularity is naturally 64k + LPVOID address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#elif HAVE(POSIX_MEMALIGN) + void* address; + posix_memalign(&address, BLOCK_SIZE, BLOCK_SIZE); + memset(address, 0, BLOCK_SIZE); +#else + static size_t pagesize = getpagesize(); + + size_t extra = 0; + if (BLOCK_SIZE > pagesize) + extra = BLOCK_SIZE - pagesize; + + void* mmapResult = mmap(NULL, BLOCK_SIZE + extra, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + uintptr_t address = reinterpret_cast<uintptr_t>(mmapResult); + + size_t adjust = 0; + if ((address & BLOCK_OFFSET_MASK) != 0) + adjust = BLOCK_SIZE - (address & BLOCK_OFFSET_MASK); + + if (adjust > 0) + munmap(reinterpret_cast<void*>(address), adjust); + + if (adjust < extra) + munmap(reinterpret_cast<void*>(address + adjust + BLOCK_SIZE), extra - adjust); + + address += adjust; + memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE); +#endif + + return reinterpret_cast<CollectorBlock*>(address); +} + +static void freeBlock(CollectorBlock* block) +{ +#if PLATFORM(DARWIN) + vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE); +#elif PLATFORM(WIN_OS) + VirtualFree(block, BLOCK_SIZE, MEM_RELEASE); +#elif HAVE(POSIX_MEMALIGN) + free(block); +#else + munmap(block, BLOCK_SIZE); +#endif +} + +void Collector::recordExtraCost(size_t cost) +{ + // Our frequency of garbage collection tries to balance memory use against speed + // by collecting based on the number of newly created values. However, for values + // that hold on to a great deal of memory that's not in the form of other JS values, + // that is not good enough - in some cases a lot of those objects can pile up and + // use crazy amounts of memory without a GC happening. So we track these extra + // memory costs. Only unusually large objects are noted, and we only keep track + // of this extra cost until the next GC. In garbage collected languages, most values + // are either very short lived temporaries, or have extremely long lifetimes. So + // if a large value survives one garbage collection, there is not much point to + // collecting more frequently as long as it stays alive. + // NOTE: we target the primaryHeap unconditionally as JSNumber doesn't modify cost + + primaryHeap.extraCost += cost; +} + +template <Collector::HeapType heapType> struct HeapConstants; + +template <> struct HeapConstants<Collector::PrimaryHeap> { + static const size_t cellSize = CELL_SIZE; + static const size_t cellsPerBlock = CELLS_PER_BLOCK; + static const size_t bitmapShift = 0; + typedef CollectorCell Cell; + typedef CollectorBlock Block; +}; + +template <> struct HeapConstants<Collector::NumberHeap> { + static const size_t cellSize = SMALL_CELL_SIZE; + static const size_t cellsPerBlock = SMALL_CELLS_PER_BLOCK; + static const size_t bitmapShift = 1; + typedef SmallCollectorCell Cell; + typedef SmallCellCollectorBlock Block; +}; + +template <Collector::HeapType heapType> void* Collector::heapAllocate(size_t s) +{ + typedef typename HeapConstants<heapType>::Block Block; + typedef typename HeapConstants<heapType>::Cell Cell; + + CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap; + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT(s <= HeapConstants<heapType>::cellSize); + UNUSED_PARAM(s); // s is now only used for the above assert + + ASSERT(heap.operationInProgress == NoOperation); + ASSERT(heapType == PrimaryHeap || heap.extraCost == 0); + // FIXME: If another global variable access here doesn't hurt performance + // too much, we could abort() in NDEBUG builds, which could help ensure we + // don't spend any time debugging cases where we allocate inside an object's + // deallocation code. + + size_t numLiveObjects = heap.numLiveObjects; + size_t usedBlocks = heap.usedBlocks; + size_t i = heap.firstBlockWithPossibleSpace; + + // if we have a huge amount of extra cost, we'll try to collect even if we still have + // free cells left. + if (heapType == PrimaryHeap && heap.extraCost > ALLOCATIONS_PER_COLLECTION) { + size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect; + size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect; + const size_t newCost = numNewObjects + heap.extraCost; + if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect) + goto collect; + } + + ASSERT(heap.operationInProgress == NoOperation); +#ifndef NDEBUG + // FIXME: Consider doing this in NDEBUG builds too (see comment above). + heap.operationInProgress = Allocation; +#endif + +scan: + Block* targetBlock; + size_t targetBlockUsedCells; + if (i != usedBlocks) { + targetBlock = (Block*)heap.blocks[i]; + targetBlockUsedCells = targetBlock->usedCells; + ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock); + while (targetBlockUsedCells == HeapConstants<heapType>::cellsPerBlock) { + if (++i == usedBlocks) + goto collect; + targetBlock = (Block*)heap.blocks[i]; + targetBlockUsedCells = targetBlock->usedCells; + ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock); + } + heap.firstBlockWithPossibleSpace = i; + } else { + +collect: + size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect; + size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect; + const size_t newCost = numNewObjects + heap.extraCost; + + if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect) { +#ifndef NDEBUG + heap.operationInProgress = NoOperation; +#endif + bool collected = collect(); +#ifndef NDEBUG + heap.operationInProgress = Allocation; +#endif + if (collected) { + numLiveObjects = heap.numLiveObjects; + usedBlocks = heap.usedBlocks; + i = heap.firstBlockWithPossibleSpace; + goto scan; + } + } + + // didn't find a block, and GC didn't reclaim anything, need to allocate a new block + size_t numBlocks = heap.numBlocks; + if (usedBlocks == numBlocks) { + numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); + heap.numBlocks = numBlocks; + heap.blocks = static_cast<CollectorBlock **>(fastRealloc(heap.blocks, numBlocks * sizeof(CollectorBlock *))); + } + + targetBlock = (Block*)allocateBlock(); + targetBlock->freeList = targetBlock->cells; + targetBlockUsedCells = 0; + heap.blocks[usedBlocks] = (CollectorBlock*)targetBlock; + heap.usedBlocks = usedBlocks + 1; + heap.firstBlockWithPossibleSpace = usedBlocks; + } + + // find a free spot in the block and detach it from the free list + Cell *newCell = targetBlock->freeList; + + // "next" field is a cell offset -- 0 means next cell, so a zeroed block is already initialized + targetBlock->freeList = (newCell + 1) + newCell->u.freeCell.next; + + targetBlock->usedCells = static_cast<uint32_t>(targetBlockUsedCells + 1); + heap.numLiveObjects = numLiveObjects + 1; + +#ifndef NDEBUG + // FIXME: Consider doing this in NDEBUG builds too (see comment above). + heap.operationInProgress = NoOperation; +#endif + + return newCell; +} + +void* Collector::allocate(size_t s) +{ + return heapAllocate<PrimaryHeap>(s); +} + +void* Collector::allocateNumber(size_t s) +{ + return heapAllocate<NumberHeap>(s); +} + +static inline void* currentThreadStackBase() +{ +#if PLATFORM(DARWIN) + pthread_t thread = pthread_self(); + return pthread_get_stackaddr_np(thread); +#elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(MSVC) + // offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + NT_TIB* pTib; + __asm { + MOV EAX, FS:[18h] + MOV pTib, EAX + } + return (void*)pTib->StackBase; +#elif PLATFORM(WIN_OS) && PLATFORM(X86_64) && COMPILER(MSVC) + PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb()); + return (void*)pTib->StackBase; +#elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(GCC) + // offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + NT_TIB* pTib; + asm ( "movl %%fs:0x18, %0\n" + : "=r" (pTib) + ); + return (void*)pTib->StackBase; +#elif PLATFORM(SOLARIS) + stack_t s; + thr_stksegment(&s); + return s.ss_sp; +#elif PLATFORM(UNIX) + static void* stackBase = 0; + static size_t stackSize = 0; + static pthread_t stackThread; + pthread_t thread = pthread_self(); + if (stackBase == 0 || thread != stackThread) { + pthread_attr_t sattr; + pthread_attr_init(&sattr); +#if HAVE(PTHREAD_NP_H) + // e.g. on FreeBSD 5.4, neundorf@kde.org + pthread_attr_get_np(thread, &sattr); +#else + // FIXME: this function is non-portable; other POSIX systems may have different np alternatives + pthread_getattr_np(thread, &sattr); +#endif + int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); + (void)rc; // FIXME: Deal with error code somehow? Seems fatal. + ASSERT(stackBase); + pthread_attr_destroy(&sattr); + stackThread = thread; + } + return static_cast<char*>(stackBase) + stackSize; +#else +#error Need a way to get the stack base on this platform +#endif +} + +#if USE(MULTIPLE_THREADS) +static pthread_t mainThread; +#endif + +void Collector::registerAsMainThread() +{ +#if USE(MULTIPLE_THREADS) + mainThread = pthread_self(); +#endif +} + +static inline bool onMainThread() +{ +#if USE(MULTIPLE_THREADS) +#if PLATFORM(DARWIN) + return pthread_main_np(); +#else + return !!pthread_equal(pthread_self(), mainThread); +#endif +#else + return true; +#endif +} + +#if USE(MULTIPLE_THREADS) + +#if PLATFORM(DARWIN) +typedef mach_port_t PlatformThread; +#elif PLATFORM(WIN_OS) +struct PlatformThread { + PlatformThread(DWORD _id, HANDLE _handle) : id(_id), handle(_handle) {} + DWORD id; + HANDLE handle; +}; +#endif + +static inline PlatformThread getCurrentPlatformThread() +{ +#if PLATFORM(DARWIN) + return pthread_mach_thread_np(pthread_self()); +#elif PLATFORM(WIN_OS) + HANDLE threadHandle = pthread_getw32threadhandle_np(pthread_self()); + return PlatformThread(GetCurrentThreadId(), threadHandle); +#endif +} + +class Collector::Thread { +public: + Thread(pthread_t pthread, const PlatformThread& platThread, void* base) + : posixThread(pthread), platformThread(platThread), stackBase(base) {} + Thread* next; + pthread_t posixThread; + PlatformThread platformThread; + void* stackBase; +}; + +pthread_key_t registeredThreadKey; +pthread_once_t registeredThreadKeyOnce = PTHREAD_ONCE_INIT; +Collector::Thread* registeredThreads; + +static void destroyRegisteredThread(void* data) +{ + Collector::Thread* thread = (Collector::Thread*)data; + + // Can't use JSLock convenience object here because we don't want to re-register + // an exiting thread. + JSLock::lock(); + + if (registeredThreads == thread) { + registeredThreads = registeredThreads->next; + } else { + Collector::Thread *last = registeredThreads; + Collector::Thread *t; + for (t = registeredThreads->next; t != NULL; t = t->next) { + if (t == thread) { + last->next = t->next; + break; + } + last = t; + } + ASSERT(t); // If t is NULL, we never found ourselves in the list. + } + + JSLock::unlock(); + + delete thread; +} + +static void initializeRegisteredThreadKey() +{ + pthread_key_create(®isteredThreadKey, destroyRegisteredThread); +} + +void Collector::registerThread() +{ + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + + pthread_once(®isteredThreadKeyOnce, initializeRegisteredThreadKey); + + if (!pthread_getspecific(registeredThreadKey)) { +#if PLATFORM(DARWIN) + if (onMainThread()) + CollectorHeapIntrospector::init(&primaryHeap, &numberHeap); +#endif + + Collector::Thread *thread = new Collector::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase()); + + thread->next = registeredThreads; + registeredThreads = thread; + pthread_setspecific(registeredThreadKey, thread); + } +} + +#endif + +#define IS_POINTER_ALIGNED(p) (((intptr_t)(p) & (sizeof(char *) - 1)) == 0) + +// cell size needs to be a power of two for this to be valid +#define IS_HALF_CELL_ALIGNED(p) (((intptr_t)(p) & (CELL_MASK >> 1)) == 0) + +void Collector::markStackObjectsConservatively(void *start, void *end) +{ + if (start > end) { + void* tmp = start; + start = end; + end = tmp; + } + + ASSERT(((char*)end - (char*)start) < 0x1000000); + ASSERT(IS_POINTER_ALIGNED(start)); + ASSERT(IS_POINTER_ALIGNED(end)); + + char** p = (char**)start; + char** e = (char**)end; + + size_t usedPrimaryBlocks = primaryHeap.usedBlocks; + size_t usedNumberBlocks = numberHeap.usedBlocks; + CollectorBlock **primaryBlocks = primaryHeap.blocks; + CollectorBlock **numberBlocks = numberHeap.blocks; + + const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1); + + while (p != e) { + char* x = *p++; + if (IS_HALF_CELL_ALIGNED(x) && x) { + uintptr_t xAsBits = reinterpret_cast<uintptr_t>(x); + xAsBits &= CELL_ALIGN_MASK; + uintptr_t offset = xAsBits & BLOCK_OFFSET_MASK; + CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset); + // Mark the the number heap, we can mark these Cells directly to avoid the virtual call cost + for (size_t block = 0; block < usedNumberBlocks; block++) { + if ((numberBlocks[block] == blockAddr) & (offset <= lastCellOffset)) { + Collector::markCell(reinterpret_cast<JSCell*>(xAsBits)); + goto endMarkLoop; + } + } + + // Mark the primary heap + for (size_t block = 0; block < usedPrimaryBlocks; block++) { + if ((primaryBlocks[block] == blockAddr) & (offset <= lastCellOffset)) { + if (((CollectorCell*)xAsBits)->u.freeCell.zeroIfFree != 0) { + JSCell* imp = reinterpret_cast<JSCell*>(xAsBits); + if (!imp->marked()) + imp->mark(); + } + break; + } + } + endMarkLoop: + ; + } + } +} + +void Collector::markCurrentThreadConservatively() +{ + // setjmp forces volatile registers onto the stack + jmp_buf registers; +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4611) +#endif + setjmp(registers); +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + + void* dummy; + void* stackPointer = &dummy; + void* stackBase = currentThreadStackBase(); + + markStackObjectsConservatively(stackPointer, stackBase); +} + +#if USE(MULTIPLE_THREADS) + +static inline void suspendThread(const PlatformThread& platformThread) +{ +#if PLATFORM(DARWIN) + thread_suspend(platformThread); +#elif PLATFORM(WIN_OS) + SuspendThread(platformThread.handle); +#else +#error Need a way to suspend threads on this platform +#endif +} + +static inline void resumeThread(const PlatformThread& platformThread) +{ +#if PLATFORM(DARWIN) + thread_resume(platformThread); +#elif PLATFORM(WIN_OS) + ResumeThread(platformThread.handle); +#else +#error Need a way to resume threads on this platform +#endif +} + +typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit + +#if PLATFORM(DARWIN) + +#if PLATFORM(X86) +typedef i386_thread_state_t PlatformThreadRegisters; +#elif PLATFORM(X86_64) +typedef x86_thread_state64_t PlatformThreadRegisters; +#elif PLATFORM(PPC) +typedef ppc_thread_state_t PlatformThreadRegisters; +#elif PLATFORM(PPC64) +typedef ppc_thread_state64_t PlatformThreadRegisters; +#else +#error Unknown Architecture +#endif + +#elif PLATFORM(WIN_OS)&& PLATFORM(X86) +typedef CONTEXT PlatformThreadRegisters; +#else +#error Need a thread register struct for this platform +#endif + +size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs) +{ +#if PLATFORM(DARWIN) + +#if PLATFORM(X86) + unsigned user_count = sizeof(regs)/sizeof(int); + thread_state_flavor_t flavor = i386_THREAD_STATE; +#elif PLATFORM(X86_64) + unsigned user_count = x86_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = x86_THREAD_STATE64; +#elif PLATFORM(PPC) + unsigned user_count = PPC_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE; +#elif PLATFORM(PPC64) + unsigned user_count = PPC_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE64; +#else +#error Unknown Architecture +#endif + + kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); + if (result != KERN_SUCCESS) { + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, + "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result); + CRASH(); + } + return user_count * sizeof(usword_t); +// end PLATFORM(DARWIN) + +#elif PLATFORM(WIN_OS) && PLATFORM(X86) + regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; + GetThreadContext(platformThread.handle, ®s); + return sizeof(CONTEXT); +#else +#error Need a way to get thread registers on this platform +#endif +} + +static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) +{ +#if PLATFORM(DARWIN) + +#if __DARWIN_UNIX03 + +#if PLATFORM(X86) + return (void*)regs.__esp; +#elif PLATFORM(X86_64) + return (void*)regs.__rsp; +#elif PLATFORM(PPC) || PLATFORM(PPC64) + return (void*)regs.__r1; +#else +#error Unknown Architecture +#endif + +#else // !__DARWIN_UNIX03 + +#if PLATFORM(X86) + return (void*)regs.esp; +#elif PLATFORM(X86_64) + return (void*)regs.rsp; +#elif (PLATFORM(PPC) || PLATFORM(PPC64)) + return (void*)regs.r1; +#else +#error Unknown Architecture +#endif + +#endif // __DARWIN_UNIX03 + +// end PLATFORM(DARWIN) +#elif PLATFORM(X86) && PLATFORM(WIN_OS) + return (void*)(uintptr_t)regs.Esp; +#else +#error Need a way to get the stack pointer for another thread on this platform +#endif +} + +void Collector::markOtherThreadConservatively(Thread* thread) +{ + suspendThread(thread->platformThread); + + PlatformThreadRegisters regs; + size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); + + // mark the thread's registers + markStackObjectsConservatively((void*)®s, (void*)((char*)®s + regSize)); + + void* stackPointer = otherThreadStackPointer(regs); + markStackObjectsConservatively(stackPointer, thread->stackBase); + + resumeThread(thread->platformThread); +} + +#endif + +void Collector::markStackObjectsConservatively() +{ + markCurrentThreadConservatively(); + +#if USE(MULTIPLE_THREADS) + for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) { + if (!pthread_equal(thread->posixThread, pthread_self())) { + markOtherThreadConservatively(thread); + } + } +#endif +} + +typedef HashCountedSet<JSCell*> ProtectCountSet; + +static ProtectCountSet& protectedValues() +{ + static ProtectCountSet staticProtectCountSet; + return staticProtectCountSet; +} + +void Collector::protect(JSValue *k) +{ + ASSERT(k); + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (JSImmediate::isImmediate(k)) + return; + + protectedValues().add(k->asCell()); +} + +void Collector::unprotect(JSValue *k) +{ + ASSERT(k); + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (JSImmediate::isImmediate(k)) + return; + + protectedValues().remove(k->asCell()); +} + +void Collector::collectOnMainThreadOnly(JSValue* value) +{ + ASSERT(value); + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + + if (JSImmediate::isImmediate(value)) + return; + + JSCell* cell = value->asCell(); + cellBlock(cell)->collectOnMainThreadOnly.set(cellOffset(cell)); + ++mainThreadOnlyObjectCount; +} + +void Collector::markProtectedObjects() +{ + ProtectCountSet& protectedValues = KJS::protectedValues(); + ProtectCountSet::iterator end = protectedValues.end(); + for (ProtectCountSet::iterator it = protectedValues.begin(); it != end; ++it) { + JSCell *val = it->first; + if (!val->marked()) + val->mark(); + } +} + +void Collector::markMainThreadOnlyObjects() +{ +#if USE(MULTIPLE_THREADS) + ASSERT(!onMainThread()); +#endif + + // Optimization for clients that never register "main thread only" objects. + if (!mainThreadOnlyObjectCount) + return; + + // FIXME: We can optimize this marking algorithm by keeping an exact set of + // "main thread only" objects when the "main thread only" object count is + // small. We don't want to keep an exact set all the time, because WebCore + // tends to create lots of "main thread only" objects, and all that set + // thrashing can be expensive. + + size_t count = 0; + + // We don't look at the numberHeap as primitive values can never be marked as main thread only + for (size_t block = 0; block < primaryHeap.usedBlocks; block++) { + ASSERT(count < mainThreadOnlyObjectCount); + + CollectorBlock* curBlock = primaryHeap.blocks[block]; + size_t minimumCellsToProcess = curBlock->usedCells; + for (size_t i = 0; (i < minimumCellsToProcess) & (i < CELLS_PER_BLOCK); i++) { + CollectorCell* cell = curBlock->cells + i; + if (cell->u.freeCell.zeroIfFree == 0) + ++minimumCellsToProcess; + else { + if (curBlock->collectOnMainThreadOnly.get(i)) { + if (!curBlock->marked.get(i)) { + JSCell* imp = reinterpret_cast<JSCell*>(cell); + imp->mark(); + } + if (++count == mainThreadOnlyObjectCount) + return; + } + } + } + } +} + +template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThreadIsMainThread) +{ + typedef typename HeapConstants<heapType>::Block Block; + typedef typename HeapConstants<heapType>::Cell Cell; + + UNUSED_PARAM(currentThreadIsMainThread); // currentThreadIsMainThread is only used in ASSERTs + // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else + CollectorHeap& heap = heapType == Collector::PrimaryHeap ? primaryHeap : numberHeap; + + size_t emptyBlocks = 0; + size_t numLiveObjects = heap.numLiveObjects; + + for (size_t block = 0; block < heap.usedBlocks; block++) { + Block* curBlock = (Block*)heap.blocks[block]; + + size_t usedCells = curBlock->usedCells; + Cell* freeList = curBlock->freeList; + + if (usedCells == HeapConstants<heapType>::cellsPerBlock) { + // special case with a block where all cells are used -- testing indicates this happens often + for (size_t i = 0; i < HeapConstants<heapType>::cellsPerBlock; i++) { + if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) { + Cell* cell = curBlock->cells + i; + + if (heapType != Collector::NumberHeap) { + JSCell* imp = reinterpret_cast<JSCell*>(cell); + // special case for allocated but uninitialized object + // (We don't need this check earlier because nothing prior this point + // assumes the object has a valid vptr.) + if (cell->u.freeCell.zeroIfFree == 0) + continue; + + ASSERT(currentThreadIsMainThread || !curBlock->collectOnMainThreadOnly.get(i)); + if (curBlock->collectOnMainThreadOnly.get(i)) { + curBlock->collectOnMainThreadOnly.clear(i); + --Collector::mainThreadOnlyObjectCount; + } + imp->~JSCell(); + } + + --usedCells; + --numLiveObjects; + + // put cell on the free list + cell->u.freeCell.zeroIfFree = 0; + cell->u.freeCell.next = freeList - (cell + 1); + freeList = cell; + } + } + } else { + size_t minimumCellsToProcess = usedCells; + for (size_t i = 0; (i < minimumCellsToProcess) & (i < HeapConstants<heapType>::cellsPerBlock); i++) { + Cell *cell = curBlock->cells + i; + if (cell->u.freeCell.zeroIfFree == 0) { + ++minimumCellsToProcess; + } else { + if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) { + if (heapType != Collector::NumberHeap) { + JSCell *imp = reinterpret_cast<JSCell*>(cell); + ASSERT(currentThreadIsMainThread || !curBlock->collectOnMainThreadOnly.get(i)); + if (curBlock->collectOnMainThreadOnly.get(i)) { + curBlock->collectOnMainThreadOnly.clear(i); + --Collector::mainThreadOnlyObjectCount; + } + imp->~JSCell(); + } + --usedCells; + --numLiveObjects; + + // put cell on the free list + cell->u.freeCell.zeroIfFree = 0; + cell->u.freeCell.next = freeList - (cell + 1); + freeList = cell; + } + } + } + } + + curBlock->usedCells = static_cast<uint32_t>(usedCells); + curBlock->freeList = freeList; + curBlock->marked.clearAll(); + + if (usedCells == 0) { + emptyBlocks++; + if (emptyBlocks > SPARE_EMPTY_BLOCKS) { +#if !DEBUG_COLLECTOR + freeBlock((CollectorBlock*)curBlock); +#endif + // swap with the last block so we compact as we go + heap.blocks[block] = heap.blocks[heap.usedBlocks - 1]; + heap.usedBlocks--; + block--; // Don't move forward a step in this case + + if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) { + heap.numBlocks = heap.numBlocks / GROWTH_FACTOR; + heap.blocks = (CollectorBlock**)fastRealloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *)); + } + } + } + } + + if (heap.numLiveObjects != numLiveObjects) + heap.firstBlockWithPossibleSpace = 0; + + heap.numLiveObjects = numLiveObjects; + heap.numLiveObjectsAtLastCollect = numLiveObjects; + heap.extraCost = 0; + return numLiveObjects; +} + +bool Collector::collect() +{ + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + + ASSERT((primaryHeap.operationInProgress == NoOperation) | (numberHeap.operationInProgress == NoOperation)); + if ((primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation)) + abort(); + + primaryHeap.operationInProgress = Collection; + numberHeap.operationInProgress = Collection; + + bool currentThreadIsMainThread = onMainThread(); + + // MARK: first mark all referenced objects recursively starting out from the set of root objects + +#ifndef NDEBUG + // Forbid malloc during the mark phase. Marking a thread suspends it, so + // a malloc inside mark() would risk a deadlock with a thread that had been + // suspended while holding the malloc lock. + fastMallocForbid(); +#endif + + markStackObjectsConservatively(); + markProtectedObjects(); + ExecState::markActiveExecStates(); + List::markProtectedLists(); +#if USE(MULTIPLE_THREADS) + if (!currentThreadIsMainThread) + markMainThreadOnlyObjects(); +#endif + +#ifndef NDEBUG + fastMallocAllow(); +#endif + + size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects; + size_t numLiveObjects = sweep<PrimaryHeap>(currentThreadIsMainThread); + numLiveObjects += sweep<NumberHeap>(currentThreadIsMainThread); + + primaryHeap.operationInProgress = NoOperation; + numberHeap.operationInProgress = NoOperation; + + return numLiveObjects < originalLiveObjects; +} + +size_t Collector::size() +{ + return primaryHeap.numLiveObjects + numberHeap.numLiveObjects; +} + +size_t Collector::globalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject::head()) { + JSGlobalObject* o = JSGlobalObject::head(); + do { + ++count; + o = o->next(); + } while (o != JSGlobalObject::head()); + } + return count; +} + +size_t Collector::protectedGlobalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject::head()) { + JSGlobalObject* o = JSGlobalObject::head(); + do { + if (protectedValues().contains(o)) + ++count; + o = o->next(); + } while (o != JSGlobalObject::head()); + } + return count; +} + +size_t Collector::protectedObjectCount() +{ + return protectedValues().size(); +} + +static const char *typeName(JSCell *val) +{ + const char *name = "???"; + switch (val->type()) { + case UnspecifiedType: + break; + case UndefinedType: + name = "undefined"; + break; + case NullType: + name = "null"; + break; + case BooleanType: + name = "boolean"; + break; + case StringType: + name = "string"; + break; + case NumberType: + name = "number"; + break; + case ObjectType: { + const ClassInfo *info = static_cast<JSObject *>(val)->classInfo(); + name = info ? info->className : "Object"; + break; + } + case GetterSetterType: + name = "gettersetter"; + break; + } + return name; +} + +HashCountedSet<const char*>* Collector::protectedObjectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + ProtectCountSet& protectedValues = KJS::protectedValues(); + ProtectCountSet::iterator end = protectedValues.end(); + for (ProtectCountSet::iterator it = protectedValues.begin(); it != end; ++it) + counts->add(typeName(it->first)); + + return counts; +} + +bool Collector::isBusy() +{ + return (primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation); +} + +void Collector::reportOutOfMemoryToAllExecStates() +{ + ExecStateStack::const_iterator end = ExecState::activeExecStates().end(); + for (ExecStateStack::const_iterator it = ExecState::activeExecStates().begin(); it != end; ++it) { + (*it)->setException(Error::create(*it, GeneralError, "Out of memory")); + } +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/collector.h b/JavaScriptCore/kjs/collector.h new file mode 100644 index 0000000..436a6af --- /dev/null +++ b/JavaScriptCore/kjs/collector.h @@ -0,0 +1,207 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef KJSCOLLECTOR_H_ +#define KJSCOLLECTOR_H_ + +#include <string.h> +#include <wtf/HashCountedSet.h> + +namespace KJS { + + class JSCell; + class JSValue; + class CollectorBlock; + + class Collector { + public: + static void* allocate(size_t s); + static void* allocateNumber(size_t s); + static bool collect(); + static bool isBusy(); // true if an allocation or collection is in progress + + static const size_t minExtraCostSize = 256; + + static void reportExtraMemoryCost(size_t cost); + + static size_t size(); + + static void protect(JSValue*); + static void unprotect(JSValue*); + + static void collectOnMainThreadOnly(JSValue*); + + static size_t globalObjectCount(); + static size_t protectedObjectCount(); + static size_t protectedGlobalObjectCount(); + static HashCountedSet<const char*>* protectedObjectTypeCounts(); + + class Thread; + static void registerThread(); + + static void registerAsMainThread(); + + static bool isCellMarked(const JSCell*); + static void markCell(JSCell*); + + enum HeapType { PrimaryHeap, NumberHeap }; + + private: + template <Collector::HeapType heapType> static void* heapAllocate(size_t s); + template <Collector::HeapType heapType> static size_t sweep(bool); + static const CollectorBlock* cellBlock(const JSCell*); + static CollectorBlock* cellBlock(JSCell*); + static size_t cellOffset(const JSCell*); + + Collector(); + + static void recordExtraCost(size_t); + static void markProtectedObjects(); + static void markMainThreadOnlyObjects(); + static void markCurrentThreadConservatively(); + static void markOtherThreadConservatively(Thread*); + static void markStackObjectsConservatively(); + static void markStackObjectsConservatively(void* start, void* end); + + static size_t mainThreadOnlyObjectCount; + static bool memoryFull; + static void reportOutOfMemoryToAllExecStates(); + }; + + // tunable parameters + template<size_t bytesPerWord> struct CellSize; + + // cell size needs to be a power of two for certain optimizations in collector.cpp + template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 32; }; // 32-bit + template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; // 64-bit + const size_t BLOCK_SIZE = 16 * 4096; // 64k + + // derived constants + const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; + const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK; + const size_t MINIMUM_CELL_SIZE = CellSize<sizeof(void*)>::m_value; + const size_t CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0); + const size_t CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double); + const size_t SMALL_CELL_SIZE = CELL_SIZE / 2; + const size_t CELL_MASK = CELL_SIZE - 1; + const size_t CELL_ALIGN_MASK = ~CELL_MASK; + const size_t CELLS_PER_BLOCK = (BLOCK_SIZE * 8 - sizeof(uint32_t) * 8 - sizeof(void *) * 8 - 2 * (7 + 3 * 8)) / (CELL_SIZE * 8 + 2); + const size_t SMALL_CELLS_PER_BLOCK = 2 * CELLS_PER_BLOCK; + const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8; + const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); + + struct CollectorBitmap { + uint32_t bits[BITMAP_WORDS]; + bool get(size_t n) const { return !!(bits[n >> 5] & (1 << (n & 0x1F))); } + void set(size_t n) { bits[n >> 5] |= (1 << (n & 0x1F)); } + void clear(size_t n) { bits[n >> 5] &= ~(1 << (n & 0x1F)); } + void clearAll() { memset(bits, 0, sizeof(bits)); } + }; + + struct CollectorCell { + union { + double memory[CELL_ARRAY_LENGTH]; + struct { + void* zeroIfFree; + ptrdiff_t next; + } freeCell; + } u; + }; + + struct SmallCollectorCell { + union { + double memory[CELL_ARRAY_LENGTH / 2]; + struct { + void* zeroIfFree; + ptrdiff_t next; + } freeCell; + } u; + }; + + class CollectorBlock { + public: + CollectorCell cells[CELLS_PER_BLOCK]; + uint32_t usedCells; + CollectorCell* freeList; + CollectorBitmap marked; + CollectorBitmap collectOnMainThreadOnly; + }; + + class SmallCellCollectorBlock { + public: + SmallCollectorCell cells[SMALL_CELLS_PER_BLOCK]; + uint32_t usedCells; + SmallCollectorCell* freeList; + CollectorBitmap marked; + CollectorBitmap collectOnMainThreadOnly; + }; + + enum OperationInProgress { NoOperation, Allocation, Collection }; + + struct CollectorHeap { + CollectorBlock** blocks; + size_t numBlocks; + size_t usedBlocks; + size_t firstBlockWithPossibleSpace; + + size_t numLiveObjects; + size_t numLiveObjectsAtLastCollect; + size_t extraCost; + + OperationInProgress operationInProgress; + }; + + inline const CollectorBlock* Collector::cellBlock(const JSCell* cell) + { + return reinterpret_cast<const CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); + } + + inline CollectorBlock* Collector::cellBlock(JSCell* cell) + { + return const_cast<CollectorBlock*>(cellBlock(const_cast<const JSCell*>(cell))); + } + + inline size_t Collector::cellOffset(const JSCell* cell) + { + return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; + } + + inline bool Collector::isCellMarked(const JSCell* cell) + { + return cellBlock(cell)->marked.get(cellOffset(cell)); + } + + inline void Collector::markCell(JSCell* cell) + { + cellBlock(cell)->marked.set(cellOffset(cell)); + } + + inline void Collector::reportExtraMemoryCost(size_t cost) + { + if (cost > minExtraCostSize) + recordExtraCost(cost / (CELL_SIZE * 2)); + } + +} // namespace KJS + +#endif /* KJSCOLLECTOR_H_ */ diff --git a/JavaScriptCore/kjs/completion.h b/JavaScriptCore/kjs/completion.h new file mode 100644 index 0000000..09f3db7 --- /dev/null +++ b/JavaScriptCore/kjs/completion.h @@ -0,0 +1,60 @@ +// -*- c-basic-offset: 2 -*- +/* + * 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 KJS_COMPLETION_H +#define KJS_COMPLETION_H + +namespace KJS { + + class JSValue; + + enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted }; + + /** + * Completion objects are used to convey the return status and value + * from functions. + * + * See FunctionImp::execute() + * + * @see FunctionImp + * + * @short Handle for a Completion type. + */ + class Completion { + public: + Completion(ComplType type = Normal, JSValue* value = 0) + : m_type(type), m_value(value) { } + + ComplType complType() const { return m_type; } + JSValue* value() const { return m_value; } + void setValue(JSValue* v) { m_value = v; } + bool isValueCompletion() const { return !!m_value; } + + private: + ComplType m_type; + JSValue* m_value; + }; + +} + +#endif diff --git a/JavaScriptCore/kjs/config.h b/JavaScriptCore/kjs/config.h new file mode 100644 index 0000000..22ae717 --- /dev/null +++ b/JavaScriptCore/kjs/config.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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 <wtf/Platform.h> + +#if PLATFORM(MAC) +#define HAVE_JNI 1 +#endif + +#if PLATFORM(DARWIN) + +#define HAVE_ERRNO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MERGESORT 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMEB_H 1 + +#elif PLATFORM(WIN_OS) + +// If we don't define these, they get defined in windef.h. +// We want to use std::min and std::max +#define max max +#define min min + +#if !COMPILER(MSVC7) +// We need to define this before the first #include of stdlib.h or it won't contain rand_s. +#ifndef _CRT_RAND_S +#define _CRT_RAND_S +#endif +#endif + +#define HAVE_FLOAT_H 1 +#define HAVE_SYS_TIMEB_H 1 +#define HAVE_VIRTUALALLOC 1 + +#else + +/* FIXME: is this actually used or do other platforms generate their own config.h? */ + +#define HAVE_ERRNO_H 1 +#define HAVE_MMAP 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#endif + +#if PLATFORM(FREEBSD) +#define HAVE_PTHREAD_NP_H 1 +#endif + +/* FIXME: if all platforms have these, do they really need #defines? */ +#define HAVE_STDINT_H 1 +#define HAVE_STRING_H 1 + +#define WTF_CHANGES 1 + +#ifdef __cplusplus +#undef new +#undef delete +#include <wtf/FastMalloc.h> +#endif + +// this breaks compilation of <QFontDatabase>, at least, so turn it off for now +// Also generates errors on wx on Windows, because these functions +// are used from wx headers. +#if !PLATFORM(QT) && !PLATFORM(WX) +#include <wtf/DisallowCType.h> +#endif diff --git a/JavaScriptCore/kjs/create_hash_table b/JavaScriptCore/kjs/create_hash_table new file mode 100755 index 0000000..6800722 --- /dev/null +++ b/JavaScriptCore/kjs/create_hash_table @@ -0,0 +1,250 @@ +#! /usr/bin/perl -w +# +# Static Hashtable Generator +# +# (c) 2000-2002 by Harri Porten <porten@kde.org> and +# David Faure <faure@kde.org> +# Modified (c) 2004 by Nikolas Zimmermann <wildfox@kde.org> +# Copyright (C) 2007 Apple Inc. All rights reserved. +# +# Part of the KJS library. +# +# 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 +# + +use strict; + +my $file = $ARGV[0]; +shift; +my $includelookup = 0; + +# Use -i as second argument to make it include "lookup.h" +$includelookup = 1 if (defined($ARGV[0]) && $ARGV[0] eq "-i"); + +# Use -n as second argument to make it use the third argument as namespace parameter ie. -n KDOM +my $useNameSpace = $ARGV[1] if (defined($ARGV[0]) && $ARGV[0] eq "-n"); + +print STDERR "Creating hashtable for $file\n"; +open(IN, $file) or die "No such file $file"; + +my @keys = (); +my @values = (); +my @attrs = (); +my @params = (); +my @hashes = (); +my @table = (); +my @links = (); + +my $inside = 0; +my $name; +my $size; +my $hashSizeMask; +my $banner = 0; +sub calcTable(); +sub output(); +sub hashValue($); + +while (<IN>) { + chop; + s/^\s*//g; + if (/^\#|^$/) { + # comment. do nothing + } elsif (/^\@begin/ && !$inside) { + if (/^\@begin\s*([:_\w]+)\s*\d*\s*$/) { + $inside = 1; + $name = $1; + } else { + printf STDERR "WARNING: \@begin without table name and hashsize, skipping $_\n"; + } + } elsif (/^\@end\s*$/ && $inside) { + + calcTable(); + + output(); + @keys = (); + @values = (); + @attrs = (); + @params = (); + @table = (); + @links = (); + @hashes = (); + $inside = 0; + } elsif (/^(\S+)\s*(\S+)\s*([\w\|]*)\s*(\w*)\s*$/ && $inside) { + my $key = $1; + my $val = $2; + my $att = $3; + my $param = $4; + push(@keys, $key); + push(@values, $val); + push(@hashes, hashValue($key)); + printf STDERR "WARNING: Number of arguments missing for $key/$val\n" + if ( $att =~ m/Function/ && length($param) == 0); + push(@attrs, length($att) > 0 ? $att : "0"); + push(@params, length($param) > 0 ? $param : "0"); + } elsif ($inside) { + die "invalid data {" . $_ . "}"; + } +} + +die "missing closing \@end" if ($inside); + +sub ceilingToPowerOf2 +{ + my ($size) = @_; + + my $powerOf2 = 1; + while ($size > $powerOf2) { + $powerOf2 <<= 1; + } + + return $powerOf2; +} + +sub calcTable() { + my $hashsize = ceilingToPowerOf2(2 * @keys); + $hashSizeMask = $hashsize - 1; + $size = $hashsize; + my $collisions = 0; + my $maxdepth = 0; + my $i = 0; + foreach my $key (@keys) { + my $depth = 0; + my $h = hashValue($key) % $hashsize; + while (defined($table[$h])) { + if (defined($links[$h])) { + $h = $links[$h]; + $depth++; + } else { + $collisions++; + $links[$h] = $size; + $h = $size; + $size++; + } + } + $table[$h] = $i; + $i++; + $maxdepth = $depth if ( $depth > $maxdepth); + } + + # Ensure table is big enough (in case of undef entries at the end) + if ( $#table+1 < $size ) { + $#table = $size-1; + } +} + +sub leftShift($$) { + my ($value, $distance) = @_; + return (($value << $distance) & 0xFFFFFFFF); +} + +# Paul Hsieh's SuperFastHash +# http://www.azillionmonkeys.com/qed/hash.html +# Ported from UString.. +sub hashValue($) { + my @chars = split(/ */, $_[0]); + + # This hash is designed to work on 16-bit chunks at a time. But since the normal case + # (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they + # were 16-bit chunks, which should give matching results + + my $EXP2_32 = 4294967296; + + my $hash = 0x9e3779b9; + my $l = scalar @chars; #I wish this was in Ruby --- Maks + my $rem = $l & 1; + $l = $l >> 1; + + my $s = 0; + + # Main loop + for (; $l > 0; $l--) { + $hash += ord($chars[$s]); + my $tmp = leftShift(ord($chars[$s+1]), 11) ^ $hash; + $hash = (leftShift($hash, 16)% $EXP2_32) ^ $tmp; + $s += 2; + $hash += $hash >> 11; + $hash %= $EXP2_32; + } + + # Handle end case + if ($rem !=0) { + $hash += ord($chars[$s]); + $hash ^= (leftShift($hash, 11)% $EXP2_32); + $hash += $hash >> 17; + } + + # Force "avalanching" of final 127 bits + $hash ^= leftShift($hash, 3); + $hash += ($hash >> 5); + $hash = ($hash% $EXP2_32); + $hash ^= (leftShift($hash, 2)% $EXP2_32); + $hash += ($hash >> 15); + $hash = $hash% $EXP2_32; + $hash ^= (leftShift($hash, 10)% $EXP2_32); + + # this avoids ever returning a hash code of 0, since that is used to + # signal "hash not computed yet", using a value that is likely to be + # effectively the same as 0 when the low bits are masked + $hash = 0x80000000 if ($hash == 0); + + return $hash; +} + +sub output() { + if (!$banner) { + $banner = 1; + print "/* Automatically generated from $file using $0. DO NOT EDIT ! */\n"; + } + + my $nameEntries = "${name}Entries"; + $nameEntries =~ s/:/_/g; + + print "\n#include \"lookup.h\"\n" if ($includelookup); + if ($useNameSpace) { + print "\nnamespace ${useNameSpace}\n{\n"; + print "\nusing namespace KJS;"; + } else { + print "\nnamespace KJS {\n"; + } + print "\nstatic const struct HashEntry ".$nameEntries."[] = {\n"; + my $i = 0; + + foreach my $entry (@table) { + if (defined($entry)) { + my $key = $keys[$entry]; + print " \{ \"" . $key . "\""; + print ", { (intptr_t)" . $values[$entry] . " }"; + print ", " . $attrs[$entry]; + print ", " . $params[$entry]; + print ", "; + if (defined($links[$i])) { + print "&" . $nameEntries . "[" . $links[$i] . "]" . " \}"; + } else { + print "0 \}" + } + print "/* " . $hashes[$entry] . " */ "; + } else { + print " { 0, { 0 }, 0, 0, 0 }"; + } + print "," unless ($i == $size - 1); + print "\n"; + $i++; + } + + print "};\n\n"; + print "const struct HashTable $name = "; + print "\{ 3, $size, $nameEntries, $hashSizeMask \};\n\n"; + print "} // namespace\n"; +} diff --git a/JavaScriptCore/kjs/date_object.cpp b/JavaScriptCore/kjs/date_object.cpp new file mode 100644 index 0000000..96e4d2b --- /dev/null +++ b/JavaScriptCore/kjs/date_object.cpp @@ -0,0 +1,1669 @@ +/* + * 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 "date_object.h" +#include "date_object.lut.h" +#include "internal.h" + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif + +#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 + +#include <float.h> +#include <limits.h> +#include <locale.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "error_object.h" +#include "operations.h" +#include "DateMath.h" + +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/UnusedParam.h> + +#if PLATFORM(MAC) + #include <CoreFoundation/CoreFoundation.h> +#endif + +using namespace WTF; + +namespace KJS { + +static double parseDate(const UString&); +static double timeClip(double); + +inline int gmtoffset(const GregorianDateTime& t) +{ + return t.utcOffset; +} + + +/** + * @internal + * + * Class to implement all methods that are properties of the + * Date object + */ +class DateObjectFuncImp : public InternalFunctionImp { +public: + DateObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier& ); + + virtual JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args); + + enum { Parse, UTC }; + +private: + int id; +}; + +#if PLATFORM(MAC) + +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 UString formatLocaleDate(ExecState *exec, double time, bool includeDate, bool includeTime, const List &args) +{ + CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); + CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); + + bool useCustomFormat = false; + UString customFormatString; + + UString arg0String = args[0]->toString(exec); + if (arg0String == "custom" && !args[1]->isUndefined()) { + useCustomFormat = true; + customFormatString = args[1]->toString(exec); + } else if (includeDate && includeTime && !args[1]->isUndefined()) { + dateStyle = styleFromArgString(arg0String, dateStyle); + timeStyle = styleFromArgString(args[1]->toString(exec), timeStyle); + } else if (includeDate && !args[0]->isUndefined()) { + dateStyle = styleFromArgString(arg0String, dateStyle); + } else if (includeTime && !args[0]->isUndefined()) { + timeStyle = styleFromArgString(arg0String, timeStyle); + } + + CFLocaleRef locale = CFLocaleCopyCurrent(); + CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle); + CFRelease(locale); + + if (useCustomFormat) { + CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, (UniChar *)customFormatString.data(), customFormatString.size()); + CFDateFormatterSetFormat(formatter, customFormatCFString); + CFRelease(customFormatCFString); + } + + CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, time - 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 = sizeof(buffer) / sizeof(buffer[0]); + size_t length = CFStringGetLength(string); + ASSERT(length <= bufferLength); + if (length > bufferLength) + length = bufferLength; + CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer)); + + CFRelease(string); + + return UString(buffer, length); +} + +#else + +enum LocaleDateTimeFormat { LocaleDateAndTime, LocaleDate, LocaleTime }; + +static JSCell* formatLocaleDate(const GregorianDateTime& gdt, const LocaleDateTimeFormat format) +{ + static const char* formatStrings[] = {"%#c", "%#x", "%X"}; + + // 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; + } + + // Do the formatting + const int bufsize=128; + char timebuffer[bufsize]; + size_t ret = strftime(timebuffer, bufsize, formatStrings[format], &localTM); + + if ( ret == 0 ) + return jsString(""); + + // 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); + } + + return jsString(timebuffer); +} + +#endif // PLATFORM(WIN_OS) + +static UString formatDate(const GregorianDateTime &t) +{ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "%s %s %02d %04d", + weekdayName[(t.weekDay + 6) % 7], + monthName[t.month], t.monthDay, t.year + 1900); + return buffer; +} + +static UString formatDateUTCVariant(const GregorianDateTime &t) +{ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d", + weekdayName[(t.weekDay + 6) % 7], + t.monthDay, monthName[t.month], t.year + 1900); + return buffer; +} + +static UString formatTime(const GregorianDateTime &t, bool utc) +{ + char buffer[100]; + if (utc) { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.hour, t.minute, t.second); + } else { + int offset = abs(gmtoffset(t)); + char tzname[70]; + struct tm gtm = t; + strftime(tzname, sizeof(tzname), "%Z", >m); + + if (tzname[0]) { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, tzname); + } else { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60); + } + } + return UString(buffer); +} + +// 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, const List& args, int maxArgs, double* ms, GregorianDateTime* t) +{ + double milliseconds = 0; + bool ok = true; + int idx = 0; + int numArgs = args.size(); + + // JS allows extra trailing arguments -- ignore them + if (numArgs > maxArgs) + numArgs = maxArgs; + + // hours + if (maxArgs >= 4 && idx < numArgs) { + t->hour = 0; + milliseconds += args[idx++]->toInt32(exec, ok) * msPerHour; + } + + // minutes + if (maxArgs >= 3 && idx < numArgs && ok) { + t->minute = 0; + milliseconds += args[idx++]->toInt32(exec, ok) * msPerMinute; + } + + // seconds + if (maxArgs >= 2 && idx < numArgs && ok) { + t->second = 0; + milliseconds += args[idx++]->toInt32(exec, ok) * msPerSecond; + } + + if (!ok) + return false; + + // milliseconds + if (idx < numArgs) { + double millis = args[idx]->toNumber(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, const List &args, int maxArgs, double *ms, GregorianDateTime *t) +{ + int idx = 0; + bool ok = true; + int numArgs = args.size(); + + // JS allows extra trailing arguments -- ignore them + if (numArgs > maxArgs) + numArgs = maxArgs; + + // years + if (maxArgs >= 3 && idx < numArgs) + t->year = args[idx++]->toInt32(exec, ok) - 1900; + + // months + if (maxArgs >= 2 && idx < numArgs && ok) + t->month = args[idx++]->toInt32(exec, ok); + + // days + if (idx < numArgs && ok) { + t->monthDay = 0; + *ms += args[idx]->toInt32(exec, ok) * msPerDay; + } + + return ok; +} + +// ------------------------------ DateInstance ------------------------------ + +const ClassInfo DateInstance::info = {"Date", 0, 0}; + +DateInstance::DateInstance(JSObject *proto) + : JSWrapperObject(proto) +{ +} + +bool DateInstance::getTime(GregorianDateTime &t, int &offset) const +{ + double milli = internalValue()->getNumber(); + if (isnan(milli)) + return false; + + msToGregorianDateTime(milli, false, t); + offset = gmtoffset(t); + return true; +} + +bool DateInstance::getUTCTime(GregorianDateTime &t) const +{ + double milli = internalValue()->getNumber(); + if (isnan(milli)) + return false; + + msToGregorianDateTime(milli, true, t); + return true; +} + +bool DateInstance::getTime(double &milli, int &offset) const +{ + milli = internalValue()->getNumber(); + if (isnan(milli)) + return false; + + GregorianDateTime t; + msToGregorianDateTime(milli, false, t); + offset = gmtoffset(t); + return true; +} + +bool DateInstance::getUTCTime(double &milli) const +{ + milli = internalValue()->getNumber(); + if (isnan(milli)) + return false; + + return true; +} + +static inline bool isTime_tSigned() +{ + time_t minusOne = (time_t)(-1); + return minusOne < 0; +} + +// ------------------------------ DatePrototype ----------------------------- + +const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, &dateTable}; + +/* Source for date_object.lut.h + FIXMEL We could use templates to simplify the UTC variants. +@begin dateTable 61 + toString dateProtoFuncToString 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 dateProtoFuncValueOf 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 +@end +*/ +// ECMA 15.9.4 + +DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto) + : DateInstance(objectProto) +{ + setInternalValue(jsNaN()); + // The constructor will be added later, after DateObjectImp has been built. +} + +bool DatePrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, &dateTable, this, propertyName, slot); +} + +// ------------------------------ DateObjectImp -------------------------------- + +// TODO: MakeTime (15.9.11.1) etc. ? + +DateObjectImp::DateObjectImp(ExecState* exec, FunctionPrototype* funcProto, DatePrototype* dateProto) + : InternalFunctionImp(funcProto, dateProto->classInfo()->className) +{ + static const Identifier* parsePropertyName = new Identifier("parse"); + static const Identifier* UTCPropertyName = new Identifier("UTC"); + + putDirect(exec->propertyNames().prototype, dateProto, DontEnum|DontDelete|ReadOnly); + putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, *parsePropertyName), DontEnum); + putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, *UTCPropertyName), DontEnum); + putDirect(exec->propertyNames().length, 7, ReadOnly|DontDelete|DontEnum); +} + +bool DateObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.9.3 +JSObject *DateObjectImp::construct(ExecState *exec, const List &args) +{ + int numArgs = args.size(); + + double value; + + if (numArgs == 0) { // new Date() ECMA 15.9.3.3 + value = getCurrentUTCTime(); + } else if (numArgs == 1) { + if (args[0]->isObject(&DateInstance::info)) + value = static_cast<DateInstance*>(args[0])->internalValue()->toNumber(exec); + else { + JSValue* primitive = args[0]->toPrimitive(exec); + if (primitive->isString()) + value = parseDate(primitive->getString()); + else + value = primitive->toNumber(exec); + } + } else { + if (isnan(args[0]->toNumber(exec)) + || isnan(args[1]->toNumber(exec)) + || (numArgs >= 3 && isnan(args[2]->toNumber(exec))) + || (numArgs >= 4 && isnan(args[3]->toNumber(exec))) + || (numArgs >= 5 && isnan(args[4]->toNumber(exec))) + || (numArgs >= 6 && isnan(args[5]->toNumber(exec))) + || (numArgs >= 7 && isnan(args[6]->toNumber(exec)))) { + value = NaN; + } else { + GregorianDateTime t; + int year = args[0]->toInt32(exec); + t.year = (year >= 0 && year <= 99) ? year : year - 1900; + t.month = args[1]->toInt32(exec); + t.monthDay = (numArgs >= 3) ? args[2]->toInt32(exec) : 1; + t.hour = args[3]->toInt32(exec); + t.minute = args[4]->toInt32(exec); + t.second = args[5]->toInt32(exec); + t.isDST = -1; + double ms = (numArgs >= 7) ? args[6]->toNumber(exec) : 0; + value = gregorianDateTimeToMS(t, ms, false); + } + } + + DateInstance *ret = new DateInstance(exec->lexicalGlobalObject()->datePrototype()); + ret->setInternalValue(jsNumber(timeClip(value))); + return ret; +} + +// ECMA 15.9.2 +JSValue *DateObjectImp::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/) +{ + time_t t = time(0); + GregorianDateTime ts(*localtime(&t)); + return jsString(formatDate(ts) + " " + formatTime(ts, false)); +} + +// ------------------------------ DateObjectFuncImp ---------------------------- + +DateObjectFuncImp::DateObjectFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name) + : InternalFunctionImp(funcProto, name), id(i) +{ + putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum); +} + +// ECMA 15.9.4.2 - 3 +JSValue *DateObjectFuncImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + if (id == Parse) { + return jsNumber(parseDate(args[0]->toString(exec))); + } + else { // UTC + int n = args.size(); + if (isnan(args[0]->toNumber(exec)) + || isnan(args[1]->toNumber(exec)) + || (n >= 3 && isnan(args[2]->toNumber(exec))) + || (n >= 4 && isnan(args[3]->toNumber(exec))) + || (n >= 5 && isnan(args[4]->toNumber(exec))) + || (n >= 6 && isnan(args[5]->toNumber(exec))) + || (n >= 7 && isnan(args[6]->toNumber(exec)))) { + return jsNaN(); + } + + GregorianDateTime t; + int year = args[0]->toInt32(exec); + t.year = (year >= 0 && year <= 99) ? year : year - 1900; + t.month = args[1]->toInt32(exec); + t.monthDay = (n >= 3) ? args[2]->toInt32(exec) : 1; + t.hour = args[3]->toInt32(exec); + t.minute = args[4]->toInt32(exec); + t.second = args[5]->toInt32(exec); + double ms = (n >= 7) ? args[6]->toNumber(exec) : 0; + return jsNumber(gregorianDateTimeToMS(t, ms, true)); + } +} + +// ----------------------------------------------------------------------------- + +// Code originally from krfcdate.cpp, but we don't want to use kdecore, and we want double range. + +static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second) +{ + double days = (day - 32075) + + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) + + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 + - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4) + - 2440588; + return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second; +} + +// We follow the recommendation of RFC 2822 to consider all +// obsolete time zones not listed here equivalent to "-0000". +static const struct KnownZone { +#if !PLATFORM(WIN_OS) + const +#endif + char tzName[4]; + int tzOffset; +} known_zones[] = { + { "UT", 0 }, + { "GMT", 0 }, + { "EST", -300 }, + { "EDT", -240 }, + { "CST", -360 }, + { "CDT", -300 }, + { "MST", -420 }, + { "MDT", -360 }, + { "PST", -480 }, + { "PDT", -420 } +}; + +inline static void skipSpacesAndComments(const char*& s) +{ + int nesting = 0; + char ch; + while ((ch = *s)) { + if (!isASCIISpace(ch)) { + if (ch == '(') + nesting++; + else if (ch == ')' && nesting > 0) + nesting--; + else if (nesting == 0) + break; + } + s++; + } +} + +// returns 0-11 (Jan-Dec); -1 on failure +static int findMonth(const char* monthStr) +{ + ASSERT(monthStr); + char needle[4]; + for (int i = 0; i < 3; ++i) { + if (!*monthStr) + return -1; + needle[i] = static_cast<char>(toASCIILower(*monthStr++)); + } + needle[3] = '\0'; + const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec"; + const char *str = strstr(haystack, needle); + if (str) { + int position = static_cast<int>(str - haystack); + if (position % 3 == 0) + return position / 3; + } + return -1; +} + +static double parseDate(const UString &date) +{ + // This parses a date in the form: + // Tuesday, 09-Nov-99 23:12:40 GMT + // or + // Sat, 01-Jan-2000 08:00:00 GMT + // or + // Sat, 01 Jan 2000 08:00:00 GMT + // or + // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) + // ### non RFC formats, added for Javascript: + // [Wednesday] January 09 1999 23:12:40 GMT + // [Wednesday] January 09 23:12:40 GMT 1999 + // + // We ignore the weekday. + + CString dateCString = date.UTF8String(); + const char *dateString = dateCString.c_str(); + + // Skip leading space + skipSpacesAndComments(dateString); + + long month = -1; + const char *wordStart = dateString; + // Check contents of first words if not number + while (*dateString && !isASCIIDigit(*dateString)) { + if (isASCIISpace(*dateString) || *dateString == '(') { + if (dateString - wordStart >= 3) + month = findMonth(wordStart); + skipSpacesAndComments(dateString); + wordStart = dateString; + } else + dateString++; + } + + // Missing delimiter between month and day (like "January29")? + if (month == -1 && wordStart != dateString) + month = findMonth(wordStart); + + skipSpacesAndComments(dateString); + + if (!*dateString) + return NaN; + + // ' 09-Nov-99 23:12:40 GMT' + char *newPosStr; + errno = 0; + long day = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + + if (!*dateString) + return NaN; + + if (day < 0) + return NaN; + + long year = 0; + if (day > 31) { + // ### where is the boundary and what happens below? + if (*dateString != '/') + return NaN; + // looks like a YYYY/MM/DD date + if (!*++dateString) + return NaN; + year = day; + month = strtol(dateString, &newPosStr, 10) - 1; + if (errno) + return NaN; + dateString = newPosStr; + if (*dateString++ != '/' || !*dateString) + return NaN; + day = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + } else if (*dateString == '/' && month == -1) { + dateString++; + // This looks like a MM/DD/YYYY date, not an RFC date. + month = day - 1; // 0-based + day = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + if (day < 1 || day > 31) + return NaN; + dateString = newPosStr; + if (*dateString == '/') + dateString++; + if (!*dateString) + return NaN; + } else { + if (*dateString == '-') + dateString++; + + skipSpacesAndComments(dateString); + + if (*dateString == ',') + dateString++; + + if (month == -1) { // not found yet + month = findMonth(dateString); + if (month == -1) + return NaN; + + while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString)) + dateString++; + + if (!*dateString) + return NaN; + + // '-99 23:12:40 GMT' + if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString)) + return NaN; + dateString++; + } + } + + if (month < 0 || month > 11) + return NaN; + + // '99 23:12:40 GMT' + if (year <= 0 && *dateString) { + year = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + } + + // Don't fail if the time is missing. + long hour = 0; + long minute = 0; + long second = 0; + if (!*newPosStr) + dateString = newPosStr; + else { + // ' 23:12:40 GMT' + if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) { + if (*newPosStr != ':') + return NaN; + // There was no year; the number was the hour. + year = -1; + } else { + // in the normal case (we parsed the year), advance to the next number + dateString = ++newPosStr; + skipSpacesAndComments(dateString); + } + + hour = strtol(dateString, &newPosStr, 10); + // Do not check for errno here since we want to continue + // even if errno was set becasue we are still looking + // for the timezone! + + // Read a number? If not, this might be a timezone name. + if (newPosStr != dateString) { + dateString = newPosStr; + + if (hour < 0 || hour > 23) + return NaN; + + if (!*dateString) + return NaN; + + // ':12:40 GMT' + if (*dateString++ != ':') + return NaN; + + minute = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + + if (minute < 0 || minute > 59) + return NaN; + + // ':40 GMT' + if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) + return NaN; + + // seconds are optional in rfc822 + rfc2822 + if (*dateString ==':') { + dateString++; + + second = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + + if (second < 0 || second > 59) + return NaN; + } + + skipSpacesAndComments(dateString); + + if (strncasecmp(dateString, "AM", 2) == 0) { + if (hour > 12) + return NaN; + if (hour == 12) + hour = 0; + dateString += 2; + skipSpacesAndComments(dateString); + } else if (strncasecmp(dateString, "PM", 2) == 0) { + if (hour > 12) + return NaN; + if (hour != 12) + hour += 12; + dateString += 2; + skipSpacesAndComments(dateString); + } + } + } + + bool haveTZ = false; + int offset = 0; + + // Don't fail if the time zone is missing. + // Some websites omit the time zone (4275206). + if (*dateString) { + if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) { + dateString += 3; + haveTZ = true; + } + + if (*dateString == '+' || *dateString == '-') { + long o = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + + if (o < -9959 || o > 9959) + return NaN; + + int sgn = (o < 0) ? -1 : 1; + o = abs(o); + if (*dateString != ':') { + offset = ((o / 100) * 60 + (o % 100)) * sgn; + } else { // GMT+05:00 + long o2 = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + offset = (o * 60 + o2) * sgn; + } + haveTZ = true; + } else { + for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) { + if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) { + offset = known_zones[i].tzOffset; + dateString += strlen(known_zones[i].tzName); + haveTZ = true; + break; + } + } + } + } + + skipSpacesAndComments(dateString); + + if (*dateString && year == -1) { + year = strtol(dateString, &newPosStr, 10); + if (errno) + return NaN; + dateString = newPosStr; + } + + skipSpacesAndComments(dateString); + + // Trailing garbage + if (*dateString) + return NaN; + + // Y2K: Handle 2 digit years. + if (year >= 0 && year < 100) { + if (year < 50) + year += 2000; + else + year += 1900; + } + + // fall back to local timezone + if (!haveTZ) { + GregorianDateTime t; + t.monthDay = day; + t.month = month; + t.year = year - 1900; + t.isDST = -1; + t.second = second; + t.minute = minute; + t.hour = hour; + + // Use our gregorianDateTimeToMS() rather than mktime() as the latter can't handle the full year range. + return gregorianDateTimeToMS(t, 0, false); + } + + return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond; +} + +double timeClip(double t) +{ + if (!isfinite(t)) + return NaN; + if (fabs(t) > 8.64E15) + return NaN; + return trunc(t); +} + +// Functions + +JSValue* dateProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsString(formatDate(t) + " " + formatTime(t, utc)); +} + +JSValue* dateProtoFuncToUTCString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsString(formatDateUTCVariant(t) + " " + formatTime(t, utc)); +} + +JSValue* dateProtoFuncToDateString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsString(formatDate(t)); +} + +JSValue* dateProtoFuncToTimeString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsString(formatTime(t, utc)); +} + +JSValue* dateProtoFuncToLocaleString(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + +#if PLATFORM(MAC) + double secs = floor(milli / msPerSecond); + return jsString(formatLocaleDate(exec, secs, true, true, args)); +#else + UNUSED_PARAM(args); + + const bool utc = false; + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return formatLocaleDate(t, LocaleDateAndTime); +#endif +} + +JSValue* dateProtoFuncToLocaleDateString(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + +#if PLATFORM(MAC) + double secs = floor(milli / msPerSecond); + return jsString(formatLocaleDate(exec, secs, true, false, args)); +#else + UNUSED_PARAM(args); + + const bool utc = false; + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return formatLocaleDate(t, LocaleDate); +#endif +} + +JSValue* dateProtoFuncToLocaleTimeString(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + +#if PLATFORM(MAC) + double secs = floor(milli / msPerSecond); + return jsString(formatLocaleDate(exec, secs, false, true, args)); +#else + UNUSED_PARAM(args); + + const bool utc = false; + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return formatLocaleDate(t, LocaleTime); +#endif +} + +JSValue* dateProtoFuncValueOf(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + return jsNumber(milli); +} + +JSValue* dateProtoFuncGetTime(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + return jsNumber(milli); +} + +JSValue* dateProtoFuncGetFullYear(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(1900 + t.year); +} + +JSValue* dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(1900 + t.year); +} + +JSValue* dateProtoFuncToGMTString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsString("Invalid Date"); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsString(formatDateUTCVariant(t) + " " + formatTime(t, utc)); +} + +JSValue* dateProtoFuncGetMonth(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.month); +} + +JSValue* dateProtoFuncGetUTCMonth(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.month); +} + +JSValue* dateProtoFuncGetDate(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.monthDay); +} + +JSValue* dateProtoFuncGetUTCDate(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.monthDay); +} + +JSValue* dateProtoFuncGetDay(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.weekDay); +} + +JSValue* dateProtoFuncGetUTCDay(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.weekDay); +} + +JSValue* dateProtoFuncGetHours(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.hour); +} + +JSValue* dateProtoFuncGetUTCHours(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.hour); +} + +JSValue* dateProtoFuncGetMinutes(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.minute); +} + +JSValue* dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.minute); +} + +JSValue* dateProtoFuncGetSeconds(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.second); +} + +JSValue* dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(t.second); +} + +JSValue* dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + return jsNumber(ms); +} + +JSValue* dateProtoFuncGetUTCMilliseconds(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + return jsNumber(ms); +} + +JSValue* dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + return jsNumber(-gmtoffset(t) / minutesPerHour); +} + +JSValue* dateProtoFuncSetTime(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + + double milli = timeClip(args[0]->toNumber(exec)); + JSValue* result = jsNumber(milli); + thisDateObj->setInternalValue(result); + return result; +} + +static JSValue* setNewValueFromTimeArgs(ExecState* exec, JSObject* thisObj, const List& args, int numArgsToUse, bool inputIsUTC) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + + if (args.isEmpty() || isnan(milli)) { + JSValue* result = jsNaN(); + thisDateObj->setInternalValue(result); + return result; + } + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + + GregorianDateTime t; + msToGregorianDateTime(milli, inputIsUTC, t); + + if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &t)) { + JSValue* result = jsNaN(); + thisDateObj->setInternalValue(result); + return result; + } + + JSValue* result = jsNumber(gregorianDateTimeToMS(t, ms, inputIsUTC)); + thisDateObj->setInternalValue(result); + return result; +} + +static JSValue* setNewValueFromDateArgs(ExecState* exec, JSObject* thisObj, const List& args, int numArgsToUse, bool inputIsUTC) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + if (args.isEmpty()) { + JSValue* result = jsNaN(); + thisDateObj->setInternalValue(result); + return result; + } + + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + double ms = 0; + + GregorianDateTime t; + if (numArgsToUse == 3 && isnan(milli)) + // Based on ECMA 262 15.9.5.40 - .41 (set[UTC]FullYear) + // the time must be reset to +0 if it is NaN. + msToGregorianDateTime(0, true, t); + else { + double secs = floor(milli / msPerSecond); + ms = milli - secs * msPerSecond; + msToGregorianDateTime(milli, inputIsUTC, t); + } + + if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &t)) { + JSValue* result = jsNaN(); + thisDateObj->setInternalValue(result); + return result; + } + + JSValue* result = jsNumber(gregorianDateTimeToMS(t, ms, inputIsUTC)); + thisDateObj->setInternalValue(result); + return result; +} + +JSValue* dateProtoFuncSetMilliSeconds(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, thisObj, args, 1, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCMilliseconds(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, thisObj, args, 1, inputIsUTC); +} + +JSValue* dateProtoFuncSetSeconds(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, thisObj, args, 2, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCSeconds(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, thisObj, args, 2, inputIsUTC); +} + +JSValue* dateProtoFuncSetMinutes(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, thisObj, args, 3, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCMinutes(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, thisObj, args, 3, inputIsUTC); +} + +JSValue* dateProtoFuncSetHours(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromTimeArgs(exec, thisObj, args, 4, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCHours(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromTimeArgs(exec, thisObj, args, 4, inputIsUTC); +} + +JSValue* dateProtoFuncSetDate(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromDateArgs(exec, thisObj, args, 1, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCDate(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromDateArgs(exec, thisObj, args, 1, inputIsUTC); +} + +JSValue* dateProtoFuncSetMonth(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromDateArgs(exec, thisObj, args, 2, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCMonth(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromDateArgs(exec, thisObj, args, 2, inputIsUTC); +} + +JSValue* dateProtoFuncSetFullYear(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = false; + return setNewValueFromDateArgs(exec, thisObj, args, 3, inputIsUTC); +} + +JSValue* dateProtoFuncSetUTCFullYear(ExecState* exec, JSObject* thisObj, const List& args) +{ + const bool inputIsUTC = true; + return setNewValueFromDateArgs(exec, thisObj, args, 3, inputIsUTC); +} + +JSValue* dateProtoFuncSetYear(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + if (args.isEmpty()) { + JSValue* result = jsNaN(); + thisDateObj->setInternalValue(result); + return result; + } + + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + double ms = 0; + + GregorianDateTime t; + if (isnan(milli)) + // Based on ECMA 262 B.2.5 (setYear) + // the time must be reset to +0 if it is NaN. + msToGregorianDateTime(0, true, t); + else { + double secs = floor(milli / msPerSecond); + ms = milli - secs * msPerSecond; + msToGregorianDateTime(milli, utc, t); + } + + bool ok = true; + int32_t year = args[0]->toInt32(exec, ok); + if (!ok) { + JSValue* result = jsNaN(); + thisDateObj->setInternalValue(result); + return result; + } + + t.year = (year > 99 || year < 0) ? year - 1900 : year; + JSValue* result = jsNumber(gregorianDateTimeToMS(t, ms, utc)); + thisDateObj->setInternalValue(result); + return result; +} + +JSValue* dateProtoFuncGetYear(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = false; + + DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); + JSValue* v = thisDateObj->internalValue(); + double milli = v->toNumber(exec); + if (isnan(milli)) + return jsNaN(); + + GregorianDateTime t; + msToGregorianDateTime(milli, utc, t); + + // NOTE: IE returns the full year even in getYear. + return jsNumber(t.year); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/date_object.h b/JavaScriptCore/kjs/date_object.h new file mode 100644 index 0000000..c545980 --- /dev/null +++ b/JavaScriptCore/kjs/date_object.h @@ -0,0 +1,135 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DATE_OBJECT_H +#define DATE_OBJECT_H + +#include "function.h" +#include "JSWrapperObject.h" +#include "lookup.h" + +namespace KJS { + + struct GregorianDateTime; + + class FunctionPrototype; + class ObjectPrototype; + + class DateInstance : public JSWrapperObject { + public: + DateInstance(JSObject *proto); + + bool getTime(GregorianDateTime&, int& offset) const; + bool getUTCTime(GregorianDateTime&) const; + bool getTime(double& milli, int& offset) const; + bool getUTCTime(double& milli) const; + + virtual const ClassInfo *classInfo() const { return &info; } + static const ClassInfo info; + }; + + /** + * @internal + * + * The initial value of Date.prototype (and thus all objects created + * with the Date constructor + */ + class DatePrototype : public DateInstance { + public: + DatePrototype(ExecState *, ObjectPrototype *); + virtual bool getOwnPropertySlot(ExecState *, const Identifier &, PropertySlot&); + virtual const ClassInfo *classInfo() const { return &info; } + static const ClassInfo info; + }; + + /** + * @internal + * + * Functions to implement all methods that are properties of the + * Date.prototype object + */ + + // Non-normative properties (Appendix B) + // GetYear, SetYear, ToGMTString + + JSValue* dateProtoFuncToString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToUTCString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToDateString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToTimeString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToLocaleString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToLocaleDateString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToLocaleTimeString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncValueOf(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetTime(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetFullYear(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCFullYear(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncToGMTString(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetMonth(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCMonth(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetDate(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCDate(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetDay(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCDay(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetHours(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCHours(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetMinutes(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCMinutes(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetSeconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCSeconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetMilliSeconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetUTCMilliseconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetTimezoneOffset(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetTime(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetMilliSeconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCMilliseconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetSeconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCSeconds(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetMinutes(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCMinutes(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetHours(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCHours(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetDate(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCDate(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetMonth(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCMonth(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetFullYear(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetUTCFullYear(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncSetYear(ExecState*, JSObject*, const List&); + JSValue* dateProtoFuncGetYear(ExecState*, JSObject*, const List&); + + /** + * @internal + * + * The initial value of the the global variable's "Date" property + */ + class DateObjectImp : public InternalFunctionImp { + public: + DateObjectImp(ExecState *, FunctionPrototype *, DatePrototype *); + + virtual bool implementsConstruct() const; + virtual JSObject *construct(ExecState *, const List &args); + virtual JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args); + + JSObject *construct(const List &); + }; + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/debugger.cpp b/JavaScriptCore/kjs/debugger.cpp new file mode 100644 index 0000000..af9c5fa --- /dev/null +++ b/JavaScriptCore/kjs/debugger.cpp @@ -0,0 +1,134 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.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 "debugger.h" + +#include "JSGlobalObject.h" +#include "internal.h" +#include "ustring.h" + +using namespace KJS; + +// ------------------------------ Debugger ------------------------------------- + +namespace KJS { + struct AttachedGlobalObject + { + public: + AttachedGlobalObject(JSGlobalObject* o, AttachedGlobalObject* ai) : globalObj(o), next(ai) { ++Debugger::debuggersPresent; } + ~AttachedGlobalObject() { --Debugger::debuggersPresent; } + JSGlobalObject* globalObj; + AttachedGlobalObject* next; + }; + +} + +int Debugger::debuggersPresent = 0; + +Debugger::Debugger() +{ + rep = new DebuggerImp(); +} + +Debugger::~Debugger() +{ + detach(0); + delete rep; +} + +void Debugger::attach(JSGlobalObject* globalObject) +{ + Debugger* other = globalObject->debugger(); + if (other == this) + return; + if (other) + other->detach(globalObject); + globalObject->setDebugger(this); + rep->globalObjects = new AttachedGlobalObject(globalObject, rep->globalObjects); +} + +void Debugger::detach(JSGlobalObject* globalObj) +{ + // iterate the addresses where AttachedGlobalObject pointers are stored + // so we can unlink items from the list + AttachedGlobalObject **p = &rep->globalObjects; + AttachedGlobalObject *q; + while ((q = *p)) { + if (!globalObj || q->globalObj == globalObj) { + *p = q->next; + q->globalObj->setDebugger(0); + delete q; + } else + p = &q->next; + } + + if (globalObj) + latestExceptions.remove(globalObj); + else + latestExceptions.clear(); +} + +bool Debugger::hasHandledException(ExecState *exec, JSValue *exception) +{ + if (latestExceptions.get(exec->dynamicGlobalObject()).get() == exception) + return true; + + latestExceptions.set(exec->dynamicGlobalObject(), exception); + return false; +} + +bool Debugger::sourceParsed(ExecState*, int /*sourceId*/, const UString &/*sourceURL*/, + const UString &/*source*/, int /*startingLineNumber*/, int /*errorLine*/, const UString & /*errorMsg*/) +{ + return true; +} + +bool Debugger::sourceUnused(ExecState*, int /*sourceId*/) +{ + return true; +} + +bool Debugger::exception(ExecState*, int /*sourceId*/, int /*lineno*/, + JSValue* /*exception */) +{ + return true; +} + +bool Debugger::atStatement(ExecState*, int /*sourceId*/, int /*firstLine*/, + int /*lastLine*/) +{ + return true; +} + +bool Debugger::callEvent(ExecState*, int /*sourceId*/, int /*lineno*/, + JSObject* /*function*/, const List &/*args*/) +{ + return true; +} + +bool Debugger::returnEvent(ExecState*, int /*sourceId*/, int /*lineno*/, + JSObject* /*function*/) +{ + return true; +} + diff --git a/JavaScriptCore/kjs/debugger.h b/JavaScriptCore/kjs/debugger.h new file mode 100644 index 0000000..2d5cb6f --- /dev/null +++ b/JavaScriptCore/kjs/debugger.h @@ -0,0 +1,225 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.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 + * + */ + +#ifndef _KJSDEBUGGER_H_ +#define _KJSDEBUGGER_H_ + +#include <wtf/HashMap.h> +#include "protect.h" + +namespace KJS { + + class DebuggerImp; + class ExecState; + class JSGlobalObject; + class JSObject; + class JSValue; + class UString; + class List; + + /** + * @internal + * + * Provides an interface which receives notification about various + * script-execution related events such as statement execution and function + * calls. + * + * WARNING: This interface is still a work in progress and is not yet + * offically publicly available. It is likely to change in binary incompatible + * (and possibly source incompatible) ways in future versions. It is + * anticipated that at some stage the interface will be frozen and made + * available for general use. + */ + class Debugger { + public: + + /** + * Creates a new debugger + */ + Debugger(); + + /** + * Destroys the debugger. If the debugger is attached to any global objects, + * it is automatically detached. + */ + virtual ~Debugger(); + + DebuggerImp *imp() const { return rep; } + + /** + * Attaches the debugger to specified global object. This will cause this + * object to receive notification of events during execution. + * + * If the global object is deleted, it will detach the debugger. + * + * Note: only one debugger can be attached to a global object at a time. + * Attaching another debugger to the same global object will cause the + * original debugger to be detached. + * + * @param The global object to attach to. + * + * @see detach() + */ + void attach(JSGlobalObject*); + + /** + * Detach the debugger from a global object. + * + * @param The global object to detach from. If 0, the debugger will be + * detached from all global objects to which it is attached. + * + * @see attach() + */ + void detach(JSGlobalObject*); + + /** + * Called to notify the debugger that some javascript source code has + * been parsed. For calls to Interpreter::evaluate(), this will be called + * with the supplied source code before any other code is parsed. + * Other situations in which this may be called include creation of a + * function using the Function() constructor, or the eval() function. + * + * The default implementation does nothing. Override this method if + * you want to process this event. + * + * @param exec The current execution state + * @param sourceId The ID of the source code (corresponds to the + * sourceId supplied in other functions such as atStatement() + * @param sourceURL Where the source code that was parsed came from + * @param source The source code that was parsed + * @param startingLineNumber The line number at which parsing started + * @param errorLine The line number at which parsing encountered an + * error, or -1 if the source code was valid and parsed successfully + * @param errorMsg The error description, or null if the source code + was valid and parsed successfully + * @return true if execution should be continue, false if it should + * be aborted + */ + virtual bool sourceParsed(ExecState *exec, int sourceId, const UString &sourceURL, + const UString &source, int startingLineNumber, int errorLine, const UString &errorMsg); + + /** + * Called when all functions/programs associated with a particular + * sourceId have been deleted. After this function has been called for + * a particular sourceId, that sourceId will not be used again. + * + * The default implementation does nothing. Override this method if + * you want to process this event. + * + * @param exec The current execution state + * @param sourceId The ID of the source code (corresponds to the + * sourceId supplied in other functions such as atLine() + * @return true if execution should be continue, false if it should + * be aborted + */ + virtual bool sourceUnused(ExecState *exec, int sourceId); + + /** + * Called when an exception is thrown during script execution. + * + * The default implementation does nothing. Override this method if + * you want to process this event. + * + * @param exec The current execution state + * @param sourceId The ID of the source code being executed + * @param lineno The line at which the error occurred + * @param exceptionObj The exception object + * @return true if execution should be continue, false if it should + * be aborted + */ + virtual bool exception(ExecState *exec, int sourceId, int lineno, + JSValue *exception); + + bool hasHandledException(ExecState *, JSValue *); + + /** + * Called when a line of the script is reached (before it is executed) + * + * The default implementation does nothing. Override this method if + * you want to process this event. + * + * @param exec The current execution state + * @param sourceId The ID of the source code being executed + * @param firstLine The starting line of the statement that is about to be + * executed + * @param lastLine The ending line of the statement that is about to be + * executed (usually the same as firstLine) + * @return true if execution should be continue, false if it should + * be aborted + */ + virtual bool atStatement(ExecState *exec, int sourceId, int firstLine, + int lastLine); + /** + * Called on each function call. Use together with @ref #returnEvent + * if you want to keep track of the call stack. + * + * Note: This only gets called for functions that are declared in ECMAScript + * source code or passed to eval(), not for internal KJS or + * application-supplied functions. + * + * The default implementation does nothing. Override this method if + * you want to process this event. + * + * @param exec The current execution state + * @param sourceId The ID of the source code being executed + * @param lineno The line that is about to be executed + * @param function The function being called + * @param args The arguments that were passed to the function + * line is being executed + * @return true if execution should be continue, false if it should + * be aborted + */ + virtual bool callEvent(ExecState *exec, int sourceId, int lineno, + JSObject *function, const List &args); + + /** + * Called on each function exit. The function being returned from is that + * which was supplied in the last callEvent(). + * + * Note: This only gets called for functions that are declared in ECMAScript + * source code or passed to eval(), not for internal KJS or + * application-supplied functions. + * + * The default implementation does nothing. Override this method if + * you want to process this event. + * + * @param exec The current execution state + * @param sourceId The ID of the source code being executed + * @param lineno The line that is about to be executed + * @param function The function being called + * @return true if execution should be continue, false if it should + * be aborted + */ + virtual bool returnEvent(ExecState *exec, int sourceId, int lineno, + JSObject *function); + + private: + DebuggerImp *rep; + HashMap<JSGlobalObject*, ProtectedPtr<JSValue> > latestExceptions; + + public: + static int debuggersPresent; + }; + +} + +#endif diff --git a/JavaScriptCore/kjs/dtoa.cpp b/JavaScriptCore/kjs/dtoa.cpp new file mode 100644 index 0000000..a8e3a68 --- /dev/null +++ b/JavaScriptCore/kjs/dtoa.cpp @@ -0,0 +1,3300 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to + David M. Gay + Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-0636 + U.S.A. + dmg@bell-labs.com + */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define INFNAN_CHECK on IEEE systems to cause strtod to check for + * Infinity and NaN (case insensitively). On some systems (e.g., + * some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define YES_ALIAS to permit aliasing certain double values with + * arrays of ULongs. This leads to slightly better code with + * some compilers and was always used prior to 19990916, but it + * is not strictly legal and can cause trouble with aggressively + * optimizing compilers (e.g., gcc 2.95.1 under -O2). + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + */ + +#include "config.h" +#include "dtoa.h" + +#if COMPILER(MSVC) +#pragma warning(disable: 4244) +#pragma warning(disable: 4245) +#pragma warning(disable: 4554) +#endif + +#if PLATFORM(BIG_ENDIAN) +#define IEEE_MC68k +#else +#define IEEE_8087 +#endif +#define INFNAN_CHECK + + + +#ifndef Long +#define Long int +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include <stdio.h> +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include <stdlib.h> +#include <string.h> + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#include <errno.h> + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include <float.h> +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include <math.h> +#endif + +#define strtod kjs_strtod +#define dtoa kjs_dtoa +#define freedtoa kjs_freedtoa + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST_ +#ifdef KR_headers +#define CONST_ /* blank */ +#else +#define CONST_ const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union { double d; ULong L[2]; } U; + +#ifdef YES_ALIAS +#define dval(x) x +#ifdef IEEE_8087 +#define word0(x) ((ULong *)&x)[1] +#define word1(x) ((ULong *)&x)[0] +#else +#define word0(x) ((ULong *)&x)[0] +#define word1(x) ((ULong *)&x)[1] +#endif +#else +#ifdef IEEE_8087 +#define word0(x) ((U*)&x)->L[1] +#define word1(x) ((U*)&x)->L[0] +#else +#define word0(x) ((U*)&x)->L[0] +#define word1(x) ((U*)&x)->L[1] +#endif +#define dval(x) ((U*)&x)->d +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#define Rounding rounding +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 15 + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; + }; + + typedef struct Bigint Bigint; + + static Bigint *freelist[Kmax+1]; + + static Bigint * +Balloc +#ifdef KR_headers + (k) int k; +#else + (int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + if ((rv = freelist[k])) { + freelist[k] = rv->next; + } + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (pmem_next - private_mem + len <= (unsigned)PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = (ULong)y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = (ULong)carry; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9) CONST_ char *s; int nd0, nd; ULong y9; +#else + (CONST_ char *s, int nd0, int nd, ULong y9) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s++; + } + else + s += 10; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; + } + + static int +hi0bits +#ifdef KR_headers + (x) register ULong x; +#else + (register ULong x) +#endif +{ + register int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits +#ifdef KR_headers + (y) ULong *y; +#else + (ULong *y) +#endif +{ + register int k; + register ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x & 1) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = (ULong)z & FFFFFFFF; + } + while(x < xae); + *xc = (ULong)carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint *p5s; + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; + } + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = (ULong)y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = (ULong)y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp +#ifdef KR_headers + (x) double x; +#else + (double x) +#endif +{ + register Long L; + double a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } + else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(a); + } + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + double d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> Ebits - k; + w = xa > xa0 ? *--xa : 0; + d1 = y << (32-Ebits) + k | w >> Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> 32 - k; + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> 32 - k; + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(d); + } + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) double d; int *e, *bits; +#else + (double d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << 32 - k; + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + double da, db; + int k, ka, kb; + + dval(da) = b2d(a, &ka); + dval(db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(da) *= 1 << k; + } + else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(db) *= 1 << k; + } +#else + if (k > 0) + word0(da) += k*Exp_msk1; + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return dval(da) / dval(db); + } + + static CONST_ double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static CONST_ double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST_ double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-53 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST_ double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST_ double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#ifndef IEEE_Arith +#undef INFNAN_CHECK +#endif + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + + static int +match +#ifdef KR_headers + (sp, t) char **sp, *t; +#else + (CONST_ char **sp, CONST_ char *t) +#endif +{ + int c, d; + CONST_ char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } + +#ifndef No_Hex_NaN + static void +hexnan +#ifdef KR_headers + (rvp, sp) double *rvp; CONST_ char **sp; +#else + (double *rvp, CONST_ char **sp) +#endif +{ + ULong c, x[2]; + CONST_ char *s; + int havedig, udx0, xshift; + + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + while((c = *(CONST_ unsigned char*)++s)) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c += 10 - 'a'; + else if (c >= 'A' && c <= 'F') + c += 10 - 'A'; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(*rvp) = Exp_mask | x[0]; + word1(*rvp) = x[1]; + } + } +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + + double +strtod +#ifdef KR_headers + (s00, se) CONST_ char *s00; char **se; +#else + (CONST_ char *s00, char **se) +#endif +{ +#ifdef Avoid_Underflow + int scale; +#endif + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST_ char *s, *s0, *s1; + double aadj, aadj1, adj, rv, rv0; + Long L; + ULong y, z; + Bigint *bb = NULL, *bb1 = NULL, *bd = NULL, *bd0 = NULL, *bs = NULL, *delta = NULL; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif + + sign = nz0 = nz = 0; + dval(rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + if (c == '.') { + c = *++s; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(rv) = 0x7ff00000; + word1(rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(rv) = NAN_WORD0; + word1(rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(rv) = tens[k - 9] * dval(rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + e -= i; + dval(rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(rv), tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(rv), tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (sign) + rounding = rounding == 2 ? 0 : 2; + else + if (rounding != 2) + rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(rv) = Big0; + word1(rv) = Big1; + break; + default: + word0(rv) = Exp_mask; + word1(rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(rv) = Exp_mask; + word1(rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(rv0) = 1e300; + dval(rv0) *= dval(rv0); +#endif +#else /*IEEE_Arith*/ + word0(rv) = Big0; + word1(rv) = Big1; +#endif /*IEEE_Arith*/ + if (bd0) + goto retfree; + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + dval(rv) *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else + word0(rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= tinytens[j]; + if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + word1(rv) = 0; + if (j >= 53) + word0(rv) = (P+2)*Exp_msk1; + else + word0(rv) &= 0xffffffff << j-32; + } + else + word1(rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(rv0) = dval(rv); + dval(rv) *= tinytens[j]; + if (!dval(rv)) { + dval(rv) = 2.*dval(rv0); + dval(rv) *= tinytens[j]; +#endif + if (!dval(rv)) { + undfl: + dval(rv) = 0.; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + if (bd0) + goto retfree; + goto ret; + } +#ifndef Avoid_Underflow + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + j = bbe - scale; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (rounding) { + if (dsign) { + adj = 1.; + goto apply_adj; + } + } + else if (!dsign) { + adj = -1.; + if (!word1(rv) + && !(word0(rv) & Frac_mask)) { + y = word0(rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) + adj = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= + P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + dval(rv) += adj*ulp(dval(rv)); + word0(rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(rv) += adj*ulp(dval(rv)); + } + break; + } + adj = ratio(delta, bs); + if (adj < 1.) + adj = 1.; + if (adj <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj; + if (y != adj) { + if (!((rounding>>1) ^ dsign)) + y++; + adj = y; + } + } +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + adj *= ulp(dval(rv)); + if (dsign) + dval(rv) += adj; + else + dval(rv) -= adj; + word0(rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + adj *= ulp(dval(rv)); + if (dsign) + dval(rv) += adj; + else + dval(rv) -= adj; + goto cont; + } +#endif /*Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(rv) & Exp_mask) <= Exp_msk1 +#endif +#endif + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == ( +#ifdef Avoid_Underflow + (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + goto undfl; + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (scale) { + L = word0(rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + dval(rv) += ulp(dval(rv)); +#ifndef ROUND_BIASED + else { + dval(rv) -= ulp(dval(rv)); +#ifndef Sudden_Underflow + if (!dval(rv)) + goto undfl; +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(Rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) + aadj1 += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(rv0) = dval(rv); + word0(rv) -= P*Exp_msk1; + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } + else + word0(rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = (ULong)aadj) <= 0) + z = 1; + aadj = z; + aadj1 = dsign ? aadj : -aadj; + } + word0(aadj1) += (2*P+1)*Exp_msk1 - y; + } + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + dval(rv0) = dval(rv); + word0(rv) += P*Exp_msk1; + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } + else + word0(rv) -= P*Exp_msk1; + } + else { + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(rv) & Exp_mask; +#ifndef SET_INEXACT +#ifdef Avoid_Underflow + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } +#endif + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(rv0) = Exp_1 + (70 << Exp_shift); + word1(rv0) = 0; + dval(rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (scale) { + word0(rv0) = Exp_1 - 2*P*Exp_msk1; + word1(rv0) = 0; + dval(rv) *= dval(rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ + if (word0(rv) == 0 && word1(rv) == 0) + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (inexact && !(word0(rv) & Exp_mask)) { + /* set underflow bit */ + dval(rv0) = 1e-300; + dval(rv0) *= dval(rv0); + } +#endif + retfree: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + ret: + if (se) + *se = (char *)s; + return sign ? -dval(rv) : dval(rv); + } + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = (ULong)y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = (ULong)y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +#ifndef MULTIPLE_THREADS + static char *dtoa_result; +#endif + + static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (unsigned)i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(CONST_ char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++)) t++; + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + char * +dtoa +#ifdef KR_headers + (d, mode, ndigits, decpt, sign, rve) + double d; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double d, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim = 0, ilim0, ilim1 = 0, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo = NULL, *mhi, *S; + double d2, ds, eps; + char *s, *s0; +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(d) && !(word0(d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(d) += 0; /* normalize */ +#endif + if (!dval(d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (*sign) + rounding = rounding == 2 ? 0 : 2; + else + if (rounding != 2) + rounding = 0; + } +#endif + + b = d2b(dval(d), &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(d2) = dval(d); + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + dval(d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32 + : word1(d) << 32 - i; + dval(d2) = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(d2) = dval(d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(d) /= ds; + } + else if ((j1 = -k)) { + dval(d) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(d) *= bigtens[i]; + } + } + if (k_check && dval(d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(d) *= 10.; + ieps++; + } + dval(eps) = ieps*dval(d) + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(d) -= 5.; + if (dval(d) > dval(eps)) + goto one_digit; + if (dval(d) < -dval(eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(eps) = 0.5/tens[ilim-1] - dval(eps); + for(i = 0;;) { + L = (long int)dval(d); + dval(d) -= L; + *s++ = '0' + (int)L; + if (dval(d) < dval(eps)) + goto ret1; + if (1. - dval(d) < dval(eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(eps) *= 10.; + dval(d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d)); + if (!(dval(d) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(d) > 0.5 + dval(eps)) + goto bump_up; + else if (dval(d) < 0.5 - dval(eps)) { + while (*--s == '0') { } + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(d) = dval(d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d) / ds); + dval(d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(d) < 0) { + L--; + dval(d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(d)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(d) += dval(d); + if (dval(d) > ds || dval(d) == ds && L & 1) { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && rounding == 1 +#endif + ) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)) + i = 32 - i; +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(d) & 1) +#ifdef Honor_FLT_ROUNDS + && rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + ) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || j1 == 0 && dig & 1) + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1); + j = cmp(b, S); + if (j > 0 || j == 0 && dig & 1) { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS +trimzeros: +#endif + while (*--s == '0') { } + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(d) = Exp_1 + (70 << Exp_shift); + word1(d) = 0; + dval(d) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } +#ifdef __cplusplus +} +#endif diff --git a/JavaScriptCore/kjs/dtoa.h b/JavaScriptCore/kjs/dtoa.h new file mode 100644 index 0000000..79ff828 --- /dev/null +++ b/JavaScriptCore/kjs/dtoa.h @@ -0,0 +1,31 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Apple Computer, 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. + * + */ + +#ifndef _KJS_DTOA_H_ +#define _KJS_DTOA_H_ + +extern "C" double kjs_strtod(const char *s00, char **se); +extern "C" char *kjs_dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +extern "C" void kjs_freedtoa(char *s); + +#endif /* _KJS_DTOA_H */ diff --git a/JavaScriptCore/kjs/error_object.cpp b/JavaScriptCore/kjs/error_object.cpp new file mode 100644 index 0000000..75ef0ba --- /dev/null +++ b/JavaScriptCore/kjs/error_object.cpp @@ -0,0 +1,153 @@ +/* + * 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 "error_object.h" + +#include "JSGlobalObject.h" +#include "object.h" +#include "operations.h" +#include "types.h" +#include "value.h" + +namespace KJS { + +// ------------------------------ ErrorInstance ---------------------------- + +const ClassInfo ErrorInstance::info = { "Error", 0, 0 }; + +ErrorInstance::ErrorInstance(JSObject* prototype) + : JSObject(prototype) +{ +} + +// ------------------------------ ErrorPrototype ---------------------------- + +// ECMA 15.9.4 +ErrorPrototype::ErrorPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype) + : ErrorInstance(objectPrototype) +{ + // The constructor will be added later in ErrorObjectImp's constructor + + putDirect(exec->propertyNames().name, jsString("Error"), DontEnum); + putDirect(exec->propertyNames().message, jsString("Unknown error"), DontEnum); + + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toString, errorProtoFuncToString), DontEnum); +} + +JSValue* errorProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + UString s = "Error"; + + JSValue* v = thisObj->get(exec, exec->propertyNames().name); + if (!v->isUndefined()) + s = v->toString(exec); + + v = thisObj->get(exec, exec->propertyNames().message); + if (!v->isUndefined()) + // Mozilla compatible format + s += ": " + v->toString(exec); + + return jsString(s); +} + +// ------------------------------ ErrorObjectImp ------------------------------- + +ErrorObjectImp::ErrorObjectImp(ExecState* exec, FunctionPrototype* funcProto, ErrorPrototype* errorProto) + : InternalFunctionImp(funcProto, errorProto->classInfo()->className) +{ + // ECMA 15.11.3.1 Error.prototype + putDirect(exec->propertyNames().prototype, errorProto, DontEnum|DontDelete|ReadOnly); + putDirect(exec->propertyNames().length, jsNumber(1), DontDelete|ReadOnly|DontEnum); +} + +bool ErrorObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.9.3 +JSObject* ErrorObjectImp::construct(ExecState* exec, const List& args) +{ + JSObject* proto = static_cast<JSObject*>(exec->lexicalGlobalObject()->errorPrototype()); + JSObject* imp = new ErrorInstance(proto); + JSObject* obj(imp); + + if (!args[0]->isUndefined()) + imp->putDirect(exec->propertyNames().message, jsString(args[0]->toString(exec))); + + return obj; +} + +// ECMA 15.9.2 +JSValue* ErrorObjectImp::callAsFunction(ExecState* exec, JSObject* /*thisObj*/, const List& args) +{ + // "Error()" gives the sames result as "new Error()" + return construct(exec, args); +} + +// ------------------------------ NativeErrorPrototype ---------------------- + +NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, ErrorPrototype* errorProto, const UString& name, const UString& message) + : JSObject(errorProto) +{ + putDirect(exec->propertyNames().name, jsString(name), 0); + putDirect(exec->propertyNames().message, jsString(message), 0); +} + +// ------------------------------ NativeErrorImp ------------------------------- + +const ClassInfo NativeErrorImp::info = { "Function", &InternalFunctionImp::info, 0 }; + +NativeErrorImp::NativeErrorImp(ExecState* exec, FunctionPrototype* funcProto, NativeErrorPrototype* prot) + : InternalFunctionImp(funcProto, Identifier(prot->getDirect(exec->propertyNames().name)->getString())) + , proto(prot) +{ + putDirect(exec->propertyNames().length, jsNumber(1), DontDelete|ReadOnly|DontEnum); // ECMA 15.11.7.5 + putDirect(exec->propertyNames().prototype, proto, DontDelete|ReadOnly|DontEnum); +} + +bool NativeErrorImp::implementsConstruct() const +{ + return true; +} + +JSObject* NativeErrorImp::construct(ExecState* exec, const List& args) +{ + JSObject* imp = new ErrorInstance(proto); + JSObject* obj(imp); + if (!args[0]->isUndefined()) + imp->putDirect(exec->propertyNames().message, jsString(args[0]->toString(exec))); + return obj; +} + +JSValue* NativeErrorImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + return construct(exec, args); +} + +void NativeErrorImp::mark() +{ + JSObject::mark(); + if (proto && !proto->marked()) + proto->mark(); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/error_object.h b/JavaScriptCore/kjs/error_object.h new file mode 100644 index 0000000..9734085 --- /dev/null +++ b/JavaScriptCore/kjs/error_object.h @@ -0,0 +1,77 @@ +/* + * 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 ERROR_OBJECT_H_ +#define ERROR_OBJECT_H_ + +#include "function_object.h" + +namespace KJS { + + class ErrorInstance : public JSObject { + public: + ErrorInstance(JSObject* prototype); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + class ErrorPrototype : public ErrorInstance { + public: + ErrorPrototype(ExecState*, ObjectPrototype*, FunctionPrototype*); + }; + + JSValue* errorProtoFuncToString(ExecState*, JSObject*, const List&); + + class ErrorObjectImp : public InternalFunctionImp { + public: + ErrorObjectImp(ExecState*, FunctionPrototype*, ErrorPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + }; + + class NativeErrorPrototype : public JSObject { + public: + NativeErrorPrototype(ExecState*, ErrorPrototype*, const UString& name, const UString& message); + }; + + class NativeErrorImp : public InternalFunctionImp { + public: + NativeErrorImp(ExecState*, FunctionPrototype*, NativeErrorPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + + virtual void mark(); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + private: + JSObject* proto; + }; + +} // namespace KJS + +#endif // ERROR_OBJECT_H_ diff --git a/JavaScriptCore/kjs/function.cpp b/JavaScriptCore/kjs/function.cpp new file mode 100644 index 0000000..b996122 --- /dev/null +++ b/JavaScriptCore/kjs/function.cpp @@ -0,0 +1,894 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 "function.h" + +#include "Activation.h" +#include "ExecState.h" +#include "JSGlobalObject.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "debugger.h" +#include "dtoa.h" +#include "function_object.h" +#include "internal.h" +#include "lexer.h" +#include "nodes.h" +#include "operations.h" +#include "scope_chain_mark.h" +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/unicode/UTF8.h> + +using namespace WTF; +using namespace Unicode; + +namespace KJS { + +// ----------------------------- FunctionImp ---------------------------------- + +const ClassInfo FunctionImp::info = { "Function", &InternalFunctionImp::info, 0 }; + +FunctionImp::FunctionImp(ExecState* exec, const Identifier& name, FunctionBodyNode* b, const ScopeChain& sc) + : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name) + , body(b) + , _scope(sc) +{ +} + +void FunctionImp::mark() +{ + InternalFunctionImp::mark(); + _scope.mark(); +} + +JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args) +{ + FunctionExecState newExec(exec->dynamicGlobalObject(), thisObj, body.get(), exec, this, args); + JSValue* result = body->execute(&newExec); + if (newExec.completionType() == Throw) { + exec->setException(result); + return result; + } + if (newExec.completionType() == ReturnValue) + return result; + return jsUndefined(); +} + +JSValue* FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) +{ + FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase()); + + for (ExecState* e = exec; e; e = e->callingExecState()) + if (e->function() == thisObj) { + e->dynamicGlobalObject()->tearOffActivation(e, e != exec); + return e->activationObject()->get(exec, propertyName); + } + + return jsNull(); +} + +JSValue* FunctionImp::callerGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot) +{ + FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase()); + ExecState* e = exec; + while (e) { + if (e->function() == thisObj) + break; + e = e->callingExecState(); + } + + if (!e) + return jsNull(); + + ExecState* callingExecState = e->callingExecState(); + if (!callingExecState) + return jsNull(); + + FunctionImp* callingFunction = callingExecState->function(); + if (!callingFunction) + return jsNull(); + + return callingFunction; +} + +JSValue* FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) +{ + FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase()); + return jsNumber(thisObj->body->parameters().size()); +} + +bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + // Find the arguments from the closest context. + if (propertyName == exec->propertyNames().arguments) { + slot.setCustom(this, argumentsGetter); + return true; + } + + // Compute length of parameters. + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + slot.setCustom(this, callerGetter); + return true; + } + + return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot); +} + +void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + return; + InternalFunctionImp::put(exec, propertyName, value); +} + +bool FunctionImp::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) + return false; + return InternalFunctionImp::deleteProperty(exec, propertyName); +} + +/* Returns the parameter name corresponding to the given index. eg: + * function f1(x, y, z): getParameterName(0) --> x + * + * If a name appears more than once, only the last index at which + * it appears associates with it. eg: + * function f2(x, x): getParameterName(0) --> null + */ +Identifier FunctionImp::getParameterName(int index) +{ + Vector<Identifier>& parameters = body->parameters(); + + if (static_cast<size_t>(index) >= body->parameters().size()) + return CommonIdentifiers::shared()->nullIdentifier; + + Identifier name = parameters[index]; + + // Are there any subsequent parameters with the same name? + size_t size = parameters.size(); + for (size_t i = index + 1; i < size; ++i) + if (parameters[i] == name) + return CommonIdentifiers::shared()->nullIdentifier; + + return name; +} + +// ECMA 13.2.2 [[Construct]] +JSObject* FunctionImp::construct(ExecState* exec, const List& args) +{ + JSObject* proto; + JSValue* p = get(exec, exec->propertyNames().prototype); + if (p->isObject()) + proto = static_cast<JSObject*>(p); + else + proto = exec->lexicalGlobalObject()->objectPrototype(); + + JSObject* obj(new JSObject(proto)); + + JSValue* res = call(exec,obj,args); + + if (res->isObject()) + return static_cast<JSObject*>(res); + else + return obj; +} + +// ------------------------------ IndexToNameMap --------------------------------- + +// We map indexes in the arguments array to their corresponding argument names. +// Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x"). + +// Once we have an argument name, we can get and set the argument's value in the +// activation object. + +// We use Identifier::null to indicate that a given argument's value +// isn't stored in the activation object. + +IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args) +{ + _map = new Identifier[args.size()]; + this->size = args.size(); + + int i = 0; + List::const_iterator end = args.end(); + for (List::const_iterator it = args.begin(); it != end; ++i, ++it) + _map[i] = func->getParameterName(i); // null if there is no corresponding parameter +} + +IndexToNameMap::~IndexToNameMap() { + delete [] _map; +} + +bool IndexToNameMap::isMapped(const Identifier& index) const +{ + bool indexIsNumber; + int indexAsNumber = index.toUInt32(&indexIsNumber); + + if (!indexIsNumber) + return false; + + if (indexAsNumber >= size) + return false; + + if (_map[indexAsNumber].isNull()) + return false; + + return true; +} + +void IndexToNameMap::unMap(const Identifier& index) +{ + bool indexIsNumber; + int indexAsNumber = index.toUInt32(&indexIsNumber); + + ASSERT(indexIsNumber && indexAsNumber < size); + + _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier; +} + +Identifier& IndexToNameMap::operator[](int index) +{ + return _map[index]; +} + +Identifier& IndexToNameMap::operator[](const Identifier& index) +{ + bool indexIsNumber; + int indexAsNumber = index.toUInt32(&indexIsNumber); + + ASSERT(indexIsNumber && indexAsNumber < size); + + return (*this)[indexAsNumber]; +} + +// ------------------------------ Arguments --------------------------------- + +const ClassInfo Arguments::info = { "Arguments", 0, 0 }; + +// ECMA 10.1.8 +Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act) + : JSObject(exec->lexicalGlobalObject()->objectPrototype()) + , _activationObject(act) + , indexToNameMap(func, args) +{ + putDirect(exec->propertyNames().callee, func, DontEnum); + putDirect(exec->propertyNames().length, args.size(), DontEnum); + + int i = 0; + List::const_iterator end = args.end(); + for (List::const_iterator it = args.begin(); it != end; ++it, ++i) { + Identifier name = Identifier::from(i); + if (!indexToNameMap.isMapped(name)) + putDirect(name, *it, DontEnum); + } +} + +void Arguments::mark() +{ + JSObject::mark(); + if (_activationObject && !_activationObject->marked()) + _activationObject->mark(); +} + +JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) +{ + Arguments* thisObj = static_cast<Arguments*>(slot.slotBase()); + return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]); +} + +bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (indexToNameMap.isMapped(propertyName)) { + slot.setCustom(this, mappedIndexGetter); + return true; + } + + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + if (indexToNameMap.isMapped(propertyName)) + _activationObject->put(exec, indexToNameMap[propertyName], value); + else + JSObject::put(exec, propertyName, value); +} + +bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (indexToNameMap.isMapped(propertyName)) { + indexToNameMap.unMap(propertyName); + return true; + } else { + return JSObject::deleteProperty(exec, propertyName); + } +} + +// ------------------------------ ActivationImp -------------------------------- + +const ClassInfo ActivationImp::info = { "Activation", 0, 0 }; + +ActivationImp::ActivationImp(const ActivationData& oldData, bool leaveRelic) +{ + JSVariableObject::d = new ActivationData(oldData); + d()->leftRelic = leaveRelic; +} + +ActivationImp::~ActivationImp() +{ + if (!d()->isOnStack) + delete d(); +} + +void ActivationImp::init(ExecState* exec) +{ + d()->symbolTable = &exec->function()->body->symbolTable(); + d()->exec = exec; + d()->function = exec->function(); + d()->argumentsObject = 0; +} + +JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot) +{ + ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase()); + + if (!thisObj->d()->argumentsObject) + thisObj->createArgumentsObject(exec); + + return thisObj->d()->argumentsObject; +} + +PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter() +{ + return ActivationImp::argumentsGetter; +} + +bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (symbolTableGet(propertyName, slot)) + return true; + + if (JSValue** location = getDirectLocation(propertyName)) { + slot.setValueSlot(this, location); + return true; + } + + // Only return the built-in arguments object if it wasn't overridden above. + if (propertyName == exec->propertyNames().arguments) { + for (ExecState* e = exec; e; e = e->callingExecState()) + if (e->function() == d()->function) { + e->dynamicGlobalObject()->tearOffActivation(e, e != exec); + ActivationImp* newActivation = e->activationObject(); + slot.setCustom(newActivation, newActivation->getArgumentsGetter()); + return true; + } + + slot.setCustom(this, getArgumentsGetter()); + 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(!_prop.hasGetterSetterProperties()); + ASSERT(prototype() == jsNull()); + return false; +} + +bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().arguments) + return false; + + return JSVariableObject::deleteProperty(exec, propertyName); +} + +void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value) +{ + if (symbolTablePut(propertyName, value)) + return; + + // We don't call through to JSObject because __proto__ and getter/setter + // properties are non-standard extensions that other implementations do not + // expose in the activation object. + ASSERT(!_prop.hasGetterSetterProperties()); + _prop.put(propertyName, value, 0, true); +} + +void ActivationImp::initializeVariable(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes) +{ + if (symbolTableInitializeVariable(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(!_prop.hasGetterSetterProperties()); + _prop.put(propertyName, value, attributes, true); +} + +void ActivationImp::markChildren() +{ + LocalStorage& localStorage = d()->localStorage; + size_t size = localStorage.size(); + + for (size_t i = 0; i < size; ++i) { + JSValue* value = localStorage[i].value; + + if (!value->marked()) + value->mark(); + } + + if (!d()->function->marked()) + d()->function->mark(); + + if (d()->argumentsObject && !d()->argumentsObject->marked()) + d()->argumentsObject->mark(); +} + +void ActivationImp::mark() +{ + JSObject::mark(); + markChildren(); +} + +void ActivationImp::createArgumentsObject(ExecState* exec) +{ + // Since "arguments" is only accessible while a function is being called, + // we can retrieve our argument list from the ExecState for our function + // call instead of storing the list ourselves. + d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this); +} + +ActivationImp::ActivationData::ActivationData(const ActivationData& old) + : JSVariableObjectData(old) + , exec(old.exec) + , function(old.function) + , argumentsObject(old.argumentsObject) + , isOnStack(false) +{ +} + +// ------------------------------ Global Functions ----------------------------------- + +static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape) +{ + UString r = "", s, str = args[0]->toString(exec); + CString cstr = str.UTF8String(true); + if (!cstr.c_str()) + return throwError(exec, URIError, "String contained an illegal UTF-16 sequence."); + const char* p = cstr.c_str(); + for (size_t k = 0; k < cstr.size(); k++, p++) { + char c = *p; + if (c && strchr(do_not_escape, c)) { + r.append(c); + } else { + char tmp[4]; + sprintf(tmp, "%%%02X", (unsigned char)c); + r += tmp; + } + } + return jsString(r); +} + +static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict) +{ + UString s = "", str = args[0]->toString(exec); + int k = 0, len = str.size(); + const UChar* d = str.data(); + UChar u; + while (k < len) { + const UChar* p = d + k; + UChar c = *p; + if (c == '%') { + int charLen = 0; + if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) { + const char b0 = Lexer::convertHex(p[1].uc, p[2].uc); + const int sequenceLen = UTF8SequenceLength(b0); + if (sequenceLen != 0 && k <= len - sequenceLen * 3) { + charLen = sequenceLen * 3; + char sequence[5]; + sequence[0] = b0; + for (int i = 1; i < sequenceLen; ++i) { + const UChar* q = p + i * 3; + if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc)) + sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc); + 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. + s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10))); + u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF)); + } else { + u = static_cast<unsigned short>(character); + } + } + } + } + if (charLen == 0) { + if (strict) + return throwError(exec, URIError); + // The only case where we don't use "strict" mode is the "unescape" function. + // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. + if (k <= len - 6 && p[1] == 'u' + && isASCIIHexDigit(p[2].uc) && isASCIIHexDigit(p[3].uc) + && isASCIIHexDigit(p[4].uc) && isASCIIHexDigit(p[5].uc)) { + charLen = 6; + u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc); + } + } + if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) { + c = u; + k += charLen - 1; + } + } + k++; + s.append(c); + } + return jsString(s); +} + +static bool isStrWhiteSpace(unsigned short c) +{ + switch (c) { + case 0x0009: + case 0x000A: + case 0x000B: + case 0x000C: + case 0x000D: + case 0x0020: + case 0x00A0: + case 0x2028: + case 0x2029: + return true; + default: + return isSeparatorSpace(c); + } +} + +static int parseDigit(unsigned short c, int radix) +{ + int digit = -1; + + if (c >= '0' && c <= '9') { + digit = c - '0'; + } else if (c >= 'A' && c <= 'Z') { + digit = c - 'A' + 10; + } else if (c >= 'a' && c <= 'z') { + digit = c - 'a' + 10; + } + + if (digit >= radix) + return -1; + return digit; +} + +double parseIntOverflow(const char* s, int length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const char* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == Inf) { + if (*p != '0') { + number = Inf; + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + +static double parseInt(const UString& s, int radix) +{ + int length = s.size(); + int p = 0; + + while (p < length && isStrWhiteSpace(s[p].uc)) { + ++p; + } + + double sign = 1; + if (p < length) { + if (s[p] == '+') { + ++p; + } else if (s[p] == '-') { + sign = -1; + ++p; + } + } + + if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) { + radix = 16; + p += 2; + } else if (radix == 0) { + if (p < length && s[p] == '0') + radix = 8; + else + radix = 10; + } + + if (radix < 2 || radix > 36) + return NaN; + + int firstDigitPosition = p; + bool sawDigit = false; + double number = 0; + while (p < length) { + int digit = parseDigit(s[p].uc, radix); + if (digit == -1) + break; + sawDigit = true; + number *= radix; + number += digit; + ++p; + } + + if (number >= mantissaOverflowLowerBound) { + if (radix == 10) + number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0); + else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) + number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix); + } + + if (!sawDigit) + return NaN; + + return sign * number; +} + +static double parseFloat(const UString& s) +{ + // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0. + // Need to skip any whitespace and then one + or - sign. + int length = s.size(); + int p = 0; + while (p < length && isStrWhiteSpace(s[p].uc)) { + ++p; + } + if (p < length && (s[p] == '+' || s[p] == '-')) { + ++p; + } + if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) { + return 0; + } + + return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ ); +} + +JSValue* globalFuncEval(ExecState* exec, JSObject* thisObj, const List& args) +{ + JSValue* x = args[0]; + if (!x->isString()) + return x; + + UString s = x->toString(exec); + + int sourceId; + int errLine; + UString errMsg; + RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg); + + Debugger* dbg = exec->dynamicGlobalObject()->debugger(); + if (dbg) { + bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg); + if (!cont) + return jsUndefined(); + } + + // No program node means a syntax occurred + if (!evalNode) + return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL); + + bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject(); + + // enter a new execution context + exec->dynamicGlobalObject()->tearOffActivation(exec); + JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject(); + EvalExecState newExec(globalObject, evalNode.get(), exec); + + if (switchGlobal) { + newExec.pushScope(thisObj); + newExec.setVariableObject(static_cast<JSGlobalObject*>(thisObj)); + } + JSValue* value = evalNode->execute(&newExec); + if (switchGlobal) + newExec.popScope(); + + if (newExec.completionType() == Throw) { + exec->setException(value); + return value; + } + + return value ? value : jsUndefined(); +} + +JSValue* globalFuncParseInt(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec))); +} + +JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(parseFloat(args[0]->toString(exec))); +} + +JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, const List& args) +{ + return jsBoolean(isnan(args[0]->toNumber(exec))); +} + +JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, const List& args) +{ + double n = args[0]->toNumber(exec); + return jsBoolean(!isnan(n) && !isinf(n)); +} + +JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, const List& args) +{ + static const char do_not_unescape_when_decoding_URI[] = + "#$&+,/:;=?@"; + + return decode(exec, args, do_not_unescape_when_decoding_URI, true); +} + +JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, const List& args) +{ + return decode(exec, args, "", true); +} + +JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, const List& args) +{ + static const char do_not_escape_when_encoding_URI[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!#$&'()*+,-./:;=?@_~"; + + return encode(exec, args, do_not_escape_when_encoding_URI); +} + +JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, const List& args) +{ + static const char do_not_escape_when_encoding_URI_component[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!'()*-._~"; + + return encode(exec, args, do_not_escape_when_encoding_URI_component); +} + +JSValue* globalFuncEscape(ExecState* exec, JSObject*, const List& args) +{ + static const char do_not_escape[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "*+-./@_"; + + UString r = "", s, str = args[0]->toString(exec); + const UChar* c = str.data(); + for (int k = 0; k < str.size(); k++, c++) { + int u = c->uc; + if (u > 255) { + char tmp[7]; + sprintf(tmp, "%%u%04X", u); + s = UString(tmp); + } else if (u != 0 && strchr(do_not_escape, (char)u)) + s = UString(c, 1); + else { + char tmp[4]; + sprintf(tmp, "%%%02X", u); + s = UString(tmp); + } + r += s; + } + + return jsString(r); +} + +JSValue* globalFuncUnescape(ExecState* exec, JSObject*, const List& args) +{ + UString s = "", str = args[0]->toString(exec); + int k = 0, len = str.size(); + while (k < len) { + const UChar* c = str.data() + k; + UChar u; + if (*c == UChar('%') && k <= len - 6 && *(c + 1) == UChar('u')) { + if (Lexer::isHexDigit((c + 2)->uc) && Lexer::isHexDigit((c + 3)->uc) && Lexer::isHexDigit((c + 4)->uc) && Lexer::isHexDigit((c + 5)->uc)) { + u = Lexer::convertUnicode((c + 2)->uc, (c + 3)->uc, (c + 4)->uc, (c + 5)->uc); + c = &u; + k += 5; + } + } else if (*c == UChar('%') && k <= len - 3 && Lexer::isHexDigit((c + 1)->uc) && Lexer::isHexDigit((c + 2)->uc)) { + u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc)); + c = &u; + k += 2; + } + k++; + s += UString(c, 1); + } + + return jsString(s); +} + +#ifndef NDEBUG +JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, const List& args) +{ + puts(args[0]->toString(exec).ascii()); + return jsUndefined(); +} +#endif + +// ------------------------------ PrototypeFunction ------------------------------- + +PrototypeFunction::PrototypeFunction(ExecState* exec, int len, const Identifier& name, JSMemberFunction function) + : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name) + , m_function(function) +{ + ASSERT_ARG(function, function); + putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum); +} + +PrototypeFunction::PrototypeFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function) + : InternalFunctionImp(functionPrototype, name) + , m_function(function) +{ + ASSERT_ARG(function, function); + putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum); +} + +JSValue* PrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args) +{ + return m_function(exec, thisObj, args); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/function.h b/JavaScriptCore/kjs/function.h new file mode 100644 index 0000000..be3e136 --- /dev/null +++ b/JavaScriptCore/kjs/function.h @@ -0,0 +1,161 @@ +// -*- c-basic-offset: 2 -*- +/* + * 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 KJS_FUNCTION_H +#define KJS_FUNCTION_H + +#include "JSVariableObject.h" +#include "LocalStorage.h" +#include "SymbolTable.h" +#include "nodes.h" +#include "object.h" + +namespace KJS { + + class ActivationImp; + class FunctionBodyNode; + class FunctionPrototype; + class JSGlobalObject; + + class InternalFunctionImp : public JSObject { + public: + InternalFunctionImp(); + InternalFunctionImp(FunctionPrototype*, const Identifier&); + + virtual bool implementsCall() const; + virtual JSValue* callAsFunction(ExecState*, JSObject* thisObjec, const List& args) = 0; + virtual bool implementsHasInstance() const; + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + const Identifier& functionName() const { return m_name; } + + private: + Identifier m_name; + }; + + class FunctionImp : public InternalFunctionImp { + friend class ActivationImp; + public: + FunctionImp(ExecState*, const Identifier& name, FunctionBodyNode*, const ScopeChain&); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue*); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + + virtual bool implementsConstruct() const { return true; } + virtual JSObject* construct(ExecState*, const List& args); + + virtual JSValue* callAsFunction(ExecState*, JSObject* thisObj, const List& args); + + // Note: unlike body->paramName, this returns Identifier::null for parameters + // that will never get set, due to later param having the same name + Identifier getParameterName(int index); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + RefPtr<FunctionBodyNode> body; + + void setScope(const ScopeChain& s) { _scope = s; } + const ScopeChain& scope() const { return _scope; } + + virtual void mark(); + + private: + ScopeChain _scope; + + static JSValue* argumentsGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + static JSValue* callerGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + static JSValue* lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + }; + + class IndexToNameMap { + public: + IndexToNameMap(FunctionImp* func, const List& args); + ~IndexToNameMap(); + + Identifier& operator[](int index); + Identifier& operator[](const Identifier &indexIdentifier); + bool isMapped(const Identifier& index) const; + void unMap(const Identifier& index); + + private: + IndexToNameMap(); // prevent construction w/o parameters + int size; + Identifier* _map; + }; + + class Arguments : public JSObject { + public: + Arguments(ExecState*, FunctionImp* func, const List& args, ActivationImp* act); + virtual void mark(); + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void put(ExecState*, const Identifier& propertyName, JSValue*); + virtual bool deleteProperty(ExecState*, const Identifier& propertyName); + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + private: + static JSValue* mappedIndexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot); + + ActivationImp* _activationObject; + mutable IndexToNameMap indexToNameMap; + }; + + class PrototypeFunction : public InternalFunctionImp { + public: + typedef KJS::JSValue* (*JSMemberFunction)(ExecState*, JSObject*, const List&); + + PrototypeFunction(ExecState*, int len, const Identifier&, JSMemberFunction); + PrototypeFunction(ExecState*, FunctionPrototype*, int len, const Identifier&, JSMemberFunction); + + virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List&); + + private: + const JSMemberFunction m_function; + }; + + + // Global Functions + JSValue* globalFuncEval(ExecState*, JSObject*, const List&); + JSValue* globalFuncParseInt(ExecState*, JSObject*, const List&); + JSValue* globalFuncParseFloat(ExecState*, JSObject*, const List&); + JSValue* globalFuncIsNaN(ExecState*, JSObject*, const List&); + JSValue* globalFuncIsFinite(ExecState*, JSObject*, const List&); + JSValue* globalFuncDecodeURI(ExecState*, JSObject*, const List&); + JSValue* globalFuncDecodeURIComponent(ExecState*, JSObject*, const List&); + JSValue* globalFuncEncodeURI(ExecState*, JSObject*, const List&); + JSValue* globalFuncEncodeURIComponent(ExecState*, JSObject*, const List&); + JSValue* globalFuncEscape(ExecState*, JSObject*, const List&); + JSValue* globalFuncUnescape(ExecState*, JSObject*, const List&); +#ifndef NDEBUG + JSValue* globalFuncKJSPrint(ExecState*, JSObject*, const List&); +#endif + + static const double mantissaOverflowLowerBound = 9007199254740992.0; + double parseIntOverflow(const char*, int length, int radix); + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/function_object.cpp b/JavaScriptCore/kjs/function_object.cpp new file mode 100644 index 0000000..3c56616 --- /dev/null +++ b/JavaScriptCore/kjs/function_object.cpp @@ -0,0 +1,239 @@ +/* + * 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 "function_object.h" + +#include "JSGlobalObject.h" +#include "Parser.h" +#include "array_object.h" +#include "debugger.h" +#include "function.h" +#include "internal.h" +#include "lexer.h" +#include "nodes.h" +#include "object.h" +#include <stdio.h> +#include <string.h> +#include <wtf/Assertions.h> + +namespace KJS { + +// ------------------------------ FunctionPrototype ------------------------- + +static JSValue* functionProtoFuncToString(ExecState*, JSObject*, const List&); +static JSValue* functionProtoFuncApply(ExecState*, JSObject*, const List&); +static JSValue* functionProtoFuncCall(ExecState*, JSObject*, const List&); + +FunctionPrototype::FunctionPrototype(ExecState* exec) +{ + static const Identifier* applyPropertyName = new Identifier("apply"); + static const Identifier* callPropertyName = new Identifier("call"); + + putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); + + putDirectFunction(new PrototypeFunction(exec, this, 0, exec->propertyNames().toString, functionProtoFuncToString), DontEnum); + putDirectFunction(new PrototypeFunction(exec, this, 2, *applyPropertyName, functionProtoFuncApply), DontEnum); + putDirectFunction(new PrototypeFunction(exec, this, 1, *callPropertyName, functionProtoFuncCall), DontEnum); +} + +// ECMA 15.3.4 +JSValue* FunctionPrototype::callAsFunction(ExecState*, JSObject*, const List&) +{ + return jsUndefined(); +} + +// Functions + +JSValue* functionProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj || !thisObj->inherits(&InternalFunctionImp::info)) { +#ifndef NDEBUG + fprintf(stderr,"attempted toString() call on null or non-function object\n"); +#endif + return throwError(exec, TypeError); + } + + if (thisObj->inherits(&FunctionImp::info)) { + FunctionImp* fi = static_cast<FunctionImp*>(thisObj); + return jsString("function " + fi->functionName().ustring() + "(" + fi->body->paramString() + ") " + fi->body->toString()); + } + + return jsString("function " + static_cast<InternalFunctionImp*>(thisObj)->functionName().ustring() + "() {\n [native code]\n}"); +} + +JSValue* functionProtoFuncApply(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->implementsCall()) + return throwError(exec, TypeError); + + JSValue* thisArg = args[0]; + JSValue* argArray = args[1]; + + JSObject* applyThis; + if (thisArg->isUndefinedOrNull()) + applyThis = exec->dynamicGlobalObject(); + else + applyThis = thisArg->toObject(exec); + + List applyArgs; + if (!argArray->isUndefinedOrNull()) { + if (argArray->isObject() && + (static_cast<JSObject*>(argArray)->inherits(&ArrayInstance::info) || + static_cast<JSObject*>(argArray)->inherits(&Arguments::info))) { + + JSObject* argArrayObj = static_cast<JSObject*>(argArray); + unsigned int length = argArrayObj->get(exec, exec->propertyNames().length)->toUInt32(exec); + for (unsigned int i = 0; i < length; i++) + applyArgs.append(argArrayObj->get(exec, i)); + } else + return throwError(exec, TypeError); + } + + return thisObj->call(exec, applyThis, applyArgs); +} + +JSValue* functionProtoFuncCall(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->implementsCall()) + return throwError(exec, TypeError); + + JSValue* thisArg = args[0]; + + JSObject* callThis; + if (thisArg->isUndefinedOrNull()) + callThis = exec->dynamicGlobalObject(); + else + callThis = thisArg->toObject(exec); + + List argsTail; + args.getSlice(1, argsTail); + return thisObj->call(exec, callThis, argsTail); +} + +// ------------------------------ FunctionObjectImp ---------------------------- + +FunctionObjectImp::FunctionObjectImp(ExecState* exec, FunctionPrototype* functionPrototype) + : InternalFunctionImp(functionPrototype, functionPrototype->classInfo()->className) +{ + putDirect(exec->propertyNames().prototype, functionPrototype, DontEnum | DontDelete | ReadOnly); + + // Number of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +bool FunctionObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.3.2 The Function Constructor +JSObject* FunctionObjectImp::construct(ExecState* exec, const List& args, const Identifier& functionName, const UString& sourceURL, int lineNumber) +{ + UString p(""); + UString body; + int argsSize = args.size(); + if (argsSize == 0) + body = ""; + else if (argsSize == 1) + body = args[0]->toString(exec); + else { + p = args[0]->toString(exec); + for (int k = 1; k < argsSize - 1; k++) + p += "," + args[k]->toString(exec); + body = args[argsSize - 1]->toString(exec); + } + + // parse the source code + int sourceId; + int errLine; + UString errMsg; + RefPtr<FunctionBodyNode> functionBody = parser().parse<FunctionBodyNode>(sourceURL, lineNumber, body.data(), body.size(), &sourceId, &errLine, &errMsg); + + // notify debugger that source has been parsed + // send empty sourceURL to indicate constructed code + Debugger* dbg = exec->dynamicGlobalObject()->debugger(); + if (dbg && !dbg->sourceParsed(exec, sourceId, UString(), body, lineNumber, errLine, errMsg)) + return new JSObject(); + + // No program node == syntax error - throw a syntax error + if (!functionBody) + // We can't return a Completion(Throw) here, so just set the exception + // and return it + return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL); + + ScopeChain scopeChain; + scopeChain.push(exec->lexicalGlobalObject()); + + FunctionImp* fimp = new FunctionImp(exec, functionName, functionBody.get(), scopeChain); + + // parse parameter list. throw syntax error on illegal identifiers + int len = p.size(); + const UChar* c = p.data(); + int i = 0, params = 0; + UString param; + while (i < len) { + while (*c == ' ' && i < len) + c++, i++; + if (Lexer::isIdentStart(c->uc)) { // else error + param = UString(c, 1); + c++, i++; + while (i < len && (Lexer::isIdentPart(c->uc))) { + param += UString(c, 1); + c++, i++; + } + while (i < len && *c == ' ') + c++, i++; + if (i == len) { + functionBody->parameters().append(Identifier(param)); + params++; + break; + } else if (*c == ',') { + functionBody->parameters().append(Identifier(param)); + params++; + c++, i++; + continue; + } // else error + } + return throwError(exec, SyntaxError, "Syntax error in parameter list"); + } + + List consArgs; + + JSObject* objCons = exec->lexicalGlobalObject()->objectConstructor(); + JSObject* prototype = objCons->construct(exec, exec->emptyList()); + prototype->putDirect(exec->propertyNames().constructor, fimp, DontEnum); + fimp->putDirect(exec->propertyNames().prototype, prototype, DontDelete); + return fimp; +} + +// ECMA 15.3.2 The Function Constructor +JSObject* FunctionObjectImp::construct(ExecState* exec, const List& args) +{ + return construct(exec, args, "anonymous", UString(), 0); +} + +// ECMA 15.3.1 The Function Constructor Called as a Function +JSValue* FunctionObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + return construct(exec, args); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/function_object.h b/JavaScriptCore/kjs/function_object.h new file mode 100644 index 0000000..cd0fe3e --- /dev/null +++ b/JavaScriptCore/kjs/function_object.h @@ -0,0 +1,61 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006 Apple Computer, 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 FUNCTION_OBJECT_H_ +#define FUNCTION_OBJECT_H_ + +#include "object_object.h" +#include "function.h" + +namespace KJS { + + /** + * @internal + * + * The initial value of Function.prototype (and thus all objects created + * with the Function constructor) + */ + class FunctionPrototype : public InternalFunctionImp { + public: + FunctionPrototype(ExecState*); + + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + }; + + /** + * @internal + * + * The initial value of the the global variable's "Function" property + */ + class FunctionObjectImp : public InternalFunctionImp { + public: + FunctionObjectImp(ExecState*, FunctionPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + virtual JSObject* construct(ExecState*, const List&, const Identifier& functionName, const UString& sourceURL, int lineNumber); + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + }; + +} // namespace KJS + +#endif // _FUNCTION_OBJECT_H_ diff --git a/JavaScriptCore/kjs/grammar.y b/JavaScriptCore/kjs/grammar.y new file mode 100644 index 0000000..658ae89 --- /dev/null +++ b/JavaScriptCore/kjs/grammar.y @@ -0,0 +1,1261 @@ +%{ + +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include "value.h" +#include "object.h" +#include "types.h" +#include "nodes.h" +#include "lexer.h" +#include "internal.h" +#include "CommonIdentifiers.h" +#include "NodeInfo.h" +#include "Parser.h" +#include <wtf/MathExtras.h> + +// Not sure why, but yacc doesn't add this define along with the others. +#define yylloc kjsyylloc + +#define YYMAXDEPTH 10000 +#define YYENABLE_NLS 0 + +/* default values for bison */ +#define YYDEBUG 0 // Set to 1 to debug a parse error. +#define kjsyydebug 0 // Set to 1 to debug a parse error. +#if !PLATFORM(DARWIN) + // avoid triggering warnings in older bison +#define YYERROR_VERBOSE +#endif + +extern int kjsyylex(); +int kjsyyerror(const char *); +static bool allowAutomaticSemicolon(); + +#define AUTO_SEMICOLON do { if (!allowAutomaticSemicolon()) YYABORT; } while (0) +#define DBG(l, s, e) (l)->setLoc((s).first_line, (e).last_line) + +using namespace KJS; +using namespace std; + +static AddNode* makeAddNode(ExpressionNode*, ExpressionNode*); +static LessNode* makeLessNode(ExpressionNode*, ExpressionNode*); +static ExpressionNode* makeAssignNode(ExpressionNode* loc, Operator, ExpressionNode* expr); +static ExpressionNode* makePrefixNode(ExpressionNode* expr, Operator); +static ExpressionNode* makePostfixNode(ExpressionNode* expr, Operator); +static PropertyNode* makeGetterOrSetterPropertyNode(const Identifier &getOrSet, const Identifier& name, ParameterNode*, FunctionBodyNode*); +static ExpressionNode* makeFunctionCallNode(ExpressionNode* func, ArgumentsNode*); +static ExpressionNode* makeTypeOfNode(ExpressionNode*); +static ExpressionNode* makeDeleteNode(ExpressionNode*); +static ExpressionNode* makeNegateNode(ExpressionNode*); +static NumberNode* makeNumberNode(double); +static StatementNode* makeVarStatementNode(ExpressionNode*); +static ExpressionNode* combineVarInitializers(ExpressionNode* list, AssignResolveNode* init); + + +#if COMPILER(MSVC) + +#pragma warning(disable: 4065) +#pragma warning(disable: 4244) +#pragma warning(disable: 4702) + +// At least some of the time, the declarations of malloc and free that bison +// generates are causing warnings. A way to avoid this is to explicitly define +// the macros so that bison doesn't try to declare malloc and free. +#define YYMALLOC malloc +#define YYFREE free + +#endif + +template <typename T> NodeInfo<T> createNodeInfo(T node, ParserRefCountedData<DeclarationStacks::VarStack>* varDecls, + ParserRefCountedData<DeclarationStacks::FunctionStack>* funcDecls) +{ + NodeInfo<T> result = {node, varDecls, funcDecls}; + return result; +} + +template <typename T> T mergeDeclarationLists(T decls1, T decls2) +{ + // decls1 or both are null + if (!decls1) + return decls2; + // only decls1 is non-null + if (!decls2) + return decls1; + + // Both are non-null + decls1->data.append(decls2->data); + + // We manually release the declaration lists to avoid accumulating many many + // unused heap allocated vectors + decls2->ref(); + decls2->deref(); + return decls1; +} + +static void appendToVarDeclarationList(ParserRefCountedData<DeclarationStacks::VarStack>*& varDecls, const Identifier& ident, unsigned attrs) +{ + if (!varDecls) + varDecls = new ParserRefCountedData<DeclarationStacks::VarStack>; + + varDecls->data.append(make_pair(ident, attrs)); + +} + +static inline void appendToVarDeclarationList(ParserRefCountedData<DeclarationStacks::VarStack>*& varDecls, ConstDeclNode* decl) +{ + unsigned attrs = DeclarationStacks::IsConstant; + if (decl->m_init) + attrs |= DeclarationStacks::HasInitializer; + appendToVarDeclarationList(varDecls, decl->m_ident, attrs); +} + +%} + +%union { + int intValue; + double doubleValue; + UString* string; + Identifier* ident; + + // expression subtrees + ExpressionNode* expressionNode; + FuncDeclNode* funcDeclNode; + PropertyNode* propertyNode; + ArgumentsNode* argumentsNode; + ConstDeclNode* constDeclNode; + CaseBlockNodeInfo caseBlockNode; + CaseClauseNodeInfo caseClauseNode; + FuncExprNode* funcExprNode; + + // statement nodes + StatementNodeInfo statementNode; + FunctionBodyNode* functionBodyNode; + ProgramNode* programNode; + + SourceElementsInfo sourceElements; + PropertyList propertyList; + ArgumentList argumentList; + VarDeclListInfo varDeclList; + ConstDeclListInfo constDeclList; + ClauseListInfo clauseList; + ElementList elementList; + ParameterList parameterList; + + Operator op; +} + +%start Program + +/* literals */ +%token NULLTOKEN TRUETOKEN FALSETOKEN + +/* keywords */ +%token BREAK CASE DEFAULT FOR NEW VAR CONSTTOKEN CONTINUE +%token FUNCTION RETURN VOIDTOKEN DELETETOKEN +%token IF THISTOKEN DO WHILE INTOKEN INSTANCEOF TYPEOF +%token SWITCH WITH RESERVED +%token THROW TRY CATCH FINALLY +%token DEBUGGER + +/* give an if without an else higher precedence than an else to resolve the ambiguity */ +%nonassoc IF_WITHOUT_ELSE +%nonassoc ELSE + +/* punctuators */ +%token EQEQ NE /* == and != */ +%token STREQ STRNEQ /* === and !== */ +%token LE GE /* < and > */ +%token OR AND /* || and && */ +%token PLUSPLUS MINUSMINUS /* ++ and -- */ +%token LSHIFT /* << */ +%token RSHIFT URSHIFT /* >> and >>> */ +%token PLUSEQUAL MINUSEQUAL /* += and -= */ +%token MULTEQUAL DIVEQUAL /* *= and /= */ +%token LSHIFTEQUAL /* <<= */ +%token RSHIFTEQUAL URSHIFTEQUAL /* >>= and >>>= */ +%token ANDEQUAL MODEQUAL /* &= and %= */ +%token XOREQUAL OREQUAL /* ^= and |= */ + +/* terminal types */ +%token <doubleValue> NUMBER +%token <string> STRING +%token <ident> IDENT + +/* automatically inserted semicolon */ +%token AUTOPLUSPLUS AUTOMINUSMINUS + +/* non-terminal types */ +%type <expressionNode> Literal ArrayLiteral + +%type <expressionNode> PrimaryExpr PrimaryExprNoBrace +%type <expressionNode> MemberExpr MemberExprNoBF /* BF => brace or function */ +%type <expressionNode> NewExpr NewExprNoBF +%type <expressionNode> CallExpr CallExprNoBF +%type <expressionNode> LeftHandSideExpr LeftHandSideExprNoBF +%type <expressionNode> PostfixExpr PostfixExprNoBF +%type <expressionNode> UnaryExpr UnaryExprNoBF UnaryExprCommon +%type <expressionNode> MultiplicativeExpr MultiplicativeExprNoBF +%type <expressionNode> AdditiveExpr AdditiveExprNoBF +%type <expressionNode> ShiftExpr ShiftExprNoBF +%type <expressionNode> RelationalExpr RelationalExprNoIn RelationalExprNoBF +%type <expressionNode> EqualityExpr EqualityExprNoIn EqualityExprNoBF +%type <expressionNode> BitwiseANDExpr BitwiseANDExprNoIn BitwiseANDExprNoBF +%type <expressionNode> BitwiseXORExpr BitwiseXORExprNoIn BitwiseXORExprNoBF +%type <expressionNode> BitwiseORExpr BitwiseORExprNoIn BitwiseORExprNoBF +%type <expressionNode> LogicalANDExpr LogicalANDExprNoIn LogicalANDExprNoBF +%type <expressionNode> LogicalORExpr LogicalORExprNoIn LogicalORExprNoBF +%type <expressionNode> ConditionalExpr ConditionalExprNoIn ConditionalExprNoBF +%type <expressionNode> AssignmentExpr AssignmentExprNoIn AssignmentExprNoBF +%type <expressionNode> Expr ExprNoIn ExprNoBF + +%type <expressionNode> ExprOpt ExprNoInOpt + +%type <statementNode> Statement Block +%type <statementNode> VariableStatement ConstStatement EmptyStatement ExprStatement +%type <statementNode> IfStatement IterationStatement ContinueStatement +%type <statementNode> BreakStatement ReturnStatement WithStatement +%type <statementNode> SwitchStatement LabelledStatement +%type <statementNode> ThrowStatement TryStatement +%type <statementNode> DebuggerStatement +%type <statementNode> SourceElement + +%type <expressionNode> Initializer InitializerNoIn +%type <funcDeclNode> FunctionDeclaration +%type <funcExprNode> FunctionExpr +%type <functionBodyNode> FunctionBody +%type <sourceElements> SourceElements +%type <parameterList> FormalParameterList +%type <op> AssignmentOperator +%type <argumentsNode> Arguments +%type <argumentList> ArgumentList +%type <varDeclList> VariableDeclarationList VariableDeclarationListNoIn +%type <constDeclList> ConstDeclarationList +%type <constDeclNode> ConstDeclaration +%type <caseBlockNode> CaseBlock +%type <caseClauseNode> CaseClause DefaultClause +%type <clauseList> CaseClauses CaseClausesOpt +%type <intValue> Elision ElisionOpt +%type <elementList> ElementList +%type <propertyNode> Property +%type <propertyList> PropertyList +%% + +Literal: + NULLTOKEN { $$ = new NullNode; } + | TRUETOKEN { $$ = new TrueNode; } + | FALSETOKEN { $$ = new FalseNode; } + | NUMBER { $$ = makeNumberNode($1); } + | STRING { $$ = new StringNode($1); } + | '/' /* regexp */ { + Lexer& l = lexer(); + if (!l.scanRegExp()) + YYABORT; + $$ = new RegExpNode(l.pattern(), l.flags()); + } + | DIVEQUAL /* regexp with /= */ { + Lexer& l = lexer(); + if (!l.scanRegExp()) + YYABORT; + $$ = new RegExpNode("=" + l.pattern(), l.flags()); + } +; + +Property: + IDENT ':' AssignmentExpr { $$ = new PropertyNode(*$1, $3, PropertyNode::Constant); } + | STRING ':' AssignmentExpr { $$ = new PropertyNode(Identifier(*$1), $3, PropertyNode::Constant); } + | NUMBER ':' AssignmentExpr { $$ = new PropertyNode(Identifier(UString::from($1)), $3, PropertyNode::Constant); } + | IDENT IDENT '(' ')' '{' FunctionBody '}' { $$ = makeGetterOrSetterPropertyNode(*$1, *$2, 0, $6); DBG($6, @5, @7); if (!$$) YYABORT; } + | IDENT IDENT '(' FormalParameterList ')' '{' FunctionBody '}' + { $$ = makeGetterOrSetterPropertyNode(*$1, *$2, $4.head, $7); DBG($7, @6, @8); if (!$$) YYABORT; } +; + +PropertyList: + Property { $$.head = new PropertyListNode($1); + $$.tail = $$.head; } + | PropertyList ',' Property { $$.head = $1.head; + $$.tail = new PropertyListNode($3, $1.tail); } +; + +PrimaryExpr: + PrimaryExprNoBrace + | '{' '}' { $$ = new ObjectLiteralNode(); } + | '{' PropertyList '}' { $$ = new ObjectLiteralNode($2.head); } + /* allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 */ + | '{' PropertyList ',' '}' { $$ = new ObjectLiteralNode($2.head); } +; + +PrimaryExprNoBrace: + THISTOKEN { $$ = new ThisNode(); } + | Literal + | ArrayLiteral + | IDENT { $$ = new ResolveNode(*$1); } + | '(' Expr ')' { $$ = $2; } +; + +ArrayLiteral: + '[' ElisionOpt ']' { $$ = new ArrayNode($2); } + | '[' ElementList ']' { $$ = new ArrayNode($2.head); } + | '[' ElementList ',' ElisionOpt ']' { $$ = new ArrayNode($4, $2.head); } +; + +ElementList: + ElisionOpt AssignmentExpr { $$.head = new ElementNode($1, $2); + $$.tail = $$.head; } + | ElementList ',' ElisionOpt AssignmentExpr + { $$.head = $1.head; + $$.tail = new ElementNode($1.tail, $3, $4); } +; + +ElisionOpt: + /* nothing */ { $$ = 0; } + | Elision +; + +Elision: + ',' { $$ = 1; } + | Elision ',' { $$ = $1 + 1; } +; + +MemberExpr: + PrimaryExpr + | FunctionExpr { $$ = $1; } + | MemberExpr '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } + | MemberExpr '.' IDENT { $$ = new DotAccessorNode($1, *$3); } + | NEW MemberExpr Arguments { $$ = new NewExprNode($2, $3); } +; + +MemberExprNoBF: + PrimaryExprNoBrace + | MemberExprNoBF '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } + | MemberExprNoBF '.' IDENT { $$ = new DotAccessorNode($1, *$3); } + | NEW MemberExpr Arguments { $$ = new NewExprNode($2, $3); } +; + +NewExpr: + MemberExpr + | NEW NewExpr { $$ = new NewExprNode($2); } +; + +NewExprNoBF: + MemberExprNoBF + | NEW NewExpr { $$ = new NewExprNode($2); } +; + +CallExpr: + MemberExpr Arguments { $$ = makeFunctionCallNode($1, $2); } + | CallExpr Arguments { $$ = makeFunctionCallNode($1, $2); } + | CallExpr '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } + | CallExpr '.' IDENT { $$ = new DotAccessorNode($1, *$3); } +; + +CallExprNoBF: + MemberExprNoBF Arguments { $$ = makeFunctionCallNode($1, $2); } + | CallExprNoBF Arguments { $$ = makeFunctionCallNode($1, $2); } + | CallExprNoBF '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } + | CallExprNoBF '.' IDENT { $$ = new DotAccessorNode($1, *$3); } +; + +Arguments: + '(' ')' { $$ = new ArgumentsNode(); } + | '(' ArgumentList ')' { $$ = new ArgumentsNode($2.head); } +; + +ArgumentList: + AssignmentExpr { $$.head = new ArgumentListNode($1); + $$.tail = $$.head; } + | ArgumentList ',' AssignmentExpr { $$.head = $1.head; + $$.tail = new ArgumentListNode($1.tail, $3); } +; + +LeftHandSideExpr: + NewExpr + | CallExpr +; + +LeftHandSideExprNoBF: + NewExprNoBF + | CallExprNoBF +; + +PostfixExpr: + LeftHandSideExpr + | LeftHandSideExpr PLUSPLUS { $$ = makePostfixNode($1, OpPlusPlus); } + | LeftHandSideExpr MINUSMINUS { $$ = makePostfixNode($1, OpMinusMinus); } +; + +PostfixExprNoBF: + LeftHandSideExprNoBF + | LeftHandSideExprNoBF PLUSPLUS { $$ = makePostfixNode($1, OpPlusPlus); } + | LeftHandSideExprNoBF MINUSMINUS { $$ = makePostfixNode($1, OpMinusMinus); } +; + +UnaryExprCommon: + DELETETOKEN UnaryExpr { $$ = makeDeleteNode($2); } + | VOIDTOKEN UnaryExpr { $$ = new VoidNode($2); } + | TYPEOF UnaryExpr { $$ = makeTypeOfNode($2); } + | PLUSPLUS UnaryExpr { $$ = makePrefixNode($2, OpPlusPlus); } + | AUTOPLUSPLUS UnaryExpr { $$ = makePrefixNode($2, OpPlusPlus); } + | MINUSMINUS UnaryExpr { $$ = makePrefixNode($2, OpMinusMinus); } + | AUTOMINUSMINUS UnaryExpr { $$ = makePrefixNode($2, OpMinusMinus); } + | '+' UnaryExpr { $$ = new UnaryPlusNode($2); } + | '-' UnaryExpr { $$ = makeNegateNode($2); } + | '~' UnaryExpr { $$ = new BitwiseNotNode($2); } + | '!' UnaryExpr { $$ = new LogicalNotNode($2); } + +UnaryExpr: + PostfixExpr + | UnaryExprCommon +; + +UnaryExprNoBF: + PostfixExprNoBF + | UnaryExprCommon +; + +MultiplicativeExpr: + UnaryExpr + | MultiplicativeExpr '*' UnaryExpr { $$ = new MultNode($1, $3); } + | MultiplicativeExpr '/' UnaryExpr { $$ = new DivNode($1, $3); } + | MultiplicativeExpr '%' UnaryExpr { $$ = new ModNode($1, $3); } +; + +MultiplicativeExprNoBF: + UnaryExprNoBF + | MultiplicativeExprNoBF '*' UnaryExpr + { $$ = new MultNode($1, $3); } + | MultiplicativeExprNoBF '/' UnaryExpr + { $$ = new DivNode($1, $3); } + | MultiplicativeExprNoBF '%' UnaryExpr + { $$ = new ModNode($1, $3); } +; + +AdditiveExpr: + MultiplicativeExpr + | AdditiveExpr '+' MultiplicativeExpr { $$ = makeAddNode($1, $3); } + | AdditiveExpr '-' MultiplicativeExpr { $$ = new SubNode($1, $3); } +; + +AdditiveExprNoBF: + MultiplicativeExprNoBF + | AdditiveExprNoBF '+' MultiplicativeExpr + { $$ = makeAddNode($1, $3); } + | AdditiveExprNoBF '-' MultiplicativeExpr + { $$ = new SubNode($1, $3); } +; + +ShiftExpr: + AdditiveExpr + | ShiftExpr LSHIFT AdditiveExpr { $$ = new LeftShiftNode($1, $3); } + | ShiftExpr RSHIFT AdditiveExpr { $$ = new RightShiftNode($1, $3); } + | ShiftExpr URSHIFT AdditiveExpr { $$ = new UnsignedRightShiftNode($1, $3); } +; + +ShiftExprNoBF: + AdditiveExprNoBF + | ShiftExprNoBF LSHIFT AdditiveExpr { $$ = new LeftShiftNode($1, $3); } + | ShiftExprNoBF RSHIFT AdditiveExpr { $$ = new RightShiftNode($1, $3); } + | ShiftExprNoBF URSHIFT AdditiveExpr { $$ = new UnsignedRightShiftNode($1, $3); } +; + +RelationalExpr: + ShiftExpr + | RelationalExpr '<' ShiftExpr { $$ = makeLessNode($1, $3); } + | RelationalExpr '>' ShiftExpr { $$ = new GreaterNode($1, $3); } + | RelationalExpr LE ShiftExpr { $$ = new LessEqNode($1, $3); } + | RelationalExpr GE ShiftExpr { $$ = new GreaterEqNode($1, $3); } + | RelationalExpr INSTANCEOF ShiftExpr { $$ = new InstanceOfNode($1, $3); } + | RelationalExpr INTOKEN ShiftExpr { $$ = new InNode($1, $3); } +; + +RelationalExprNoIn: + ShiftExpr + | RelationalExprNoIn '<' ShiftExpr { $$ = makeLessNode($1, $3); } + | RelationalExprNoIn '>' ShiftExpr { $$ = new GreaterNode($1, $3); } + | RelationalExprNoIn LE ShiftExpr { $$ = new LessEqNode($1, $3); } + | RelationalExprNoIn GE ShiftExpr { $$ = new GreaterEqNode($1, $3); } + | RelationalExprNoIn INSTANCEOF ShiftExpr + { $$ = new InstanceOfNode($1, $3); } +; + +RelationalExprNoBF: + ShiftExprNoBF + | RelationalExprNoBF '<' ShiftExpr { $$ = makeLessNode($1, $3); } + | RelationalExprNoBF '>' ShiftExpr { $$ = new GreaterNode($1, $3); } + | RelationalExprNoBF LE ShiftExpr { $$ = new LessEqNode($1, $3); } + | RelationalExprNoBF GE ShiftExpr { $$ = new GreaterEqNode($1, $3); } + | RelationalExprNoBF INSTANCEOF ShiftExpr + { $$ = new InstanceOfNode($1, $3); } + | RelationalExprNoBF INTOKEN ShiftExpr { $$ = new InNode($1, $3); } +; + +EqualityExpr: + RelationalExpr + | EqualityExpr EQEQ RelationalExpr { $$ = new EqualNode($1, $3); } + | EqualityExpr NE RelationalExpr { $$ = new NotEqualNode($1, $3); } + | EqualityExpr STREQ RelationalExpr { $$ = new StrictEqualNode($1, $3); } + | EqualityExpr STRNEQ RelationalExpr { $$ = new NotStrictEqualNode($1, $3); } +; + +EqualityExprNoIn: + RelationalExprNoIn + | EqualityExprNoIn EQEQ RelationalExprNoIn + { $$ = new EqualNode($1, $3); } + | EqualityExprNoIn NE RelationalExprNoIn + { $$ = new NotEqualNode($1, $3); } + | EqualityExprNoIn STREQ RelationalExprNoIn + { $$ = new StrictEqualNode($1, $3); } + | EqualityExprNoIn STRNEQ RelationalExprNoIn + { $$ = new NotStrictEqualNode($1, $3); } +; + +EqualityExprNoBF: + RelationalExprNoBF + | EqualityExprNoBF EQEQ RelationalExpr + { $$ = new EqualNode($1, $3); } + | EqualityExprNoBF NE RelationalExpr { $$ = new NotEqualNode($1, $3); } + | EqualityExprNoBF STREQ RelationalExpr + { $$ = new StrictEqualNode($1, $3); } + | EqualityExprNoBF STRNEQ RelationalExpr + { $$ = new NotStrictEqualNode($1, $3); } +; + +BitwiseANDExpr: + EqualityExpr + | BitwiseANDExpr '&' EqualityExpr { $$ = new BitAndNode($1, $3); } +; + +BitwiseANDExprNoIn: + EqualityExprNoIn + | BitwiseANDExprNoIn '&' EqualityExprNoIn + { $$ = new BitAndNode($1, $3); } +; + +BitwiseANDExprNoBF: + EqualityExprNoBF + | BitwiseANDExprNoBF '&' EqualityExpr { $$ = new BitAndNode($1, $3); } +; + +BitwiseXORExpr: + BitwiseANDExpr + | BitwiseXORExpr '^' BitwiseANDExpr { $$ = new BitXOrNode($1, $3); } +; + +BitwiseXORExprNoIn: + BitwiseANDExprNoIn + | BitwiseXORExprNoIn '^' BitwiseANDExprNoIn + { $$ = new BitXOrNode($1, $3); } +; + +BitwiseXORExprNoBF: + BitwiseANDExprNoBF + | BitwiseXORExprNoBF '^' BitwiseANDExpr + { $$ = new BitXOrNode($1, $3); } +; + +BitwiseORExpr: + BitwiseXORExpr + | BitwiseORExpr '|' BitwiseXORExpr { $$ = new BitOrNode($1, $3); } +; + +BitwiseORExprNoIn: + BitwiseXORExprNoIn + | BitwiseORExprNoIn '|' BitwiseXORExprNoIn + { $$ = new BitOrNode($1, $3); } +; + +BitwiseORExprNoBF: + BitwiseXORExprNoBF + | BitwiseORExprNoBF '|' BitwiseXORExpr + { $$ = new BitOrNode($1, $3); } +; + +LogicalANDExpr: + BitwiseORExpr + | LogicalANDExpr AND BitwiseORExpr { $$ = new LogicalAndNode($1, $3); } +; + +LogicalANDExprNoIn: + BitwiseORExprNoIn + | LogicalANDExprNoIn AND BitwiseORExprNoIn + { $$ = new LogicalAndNode($1, $3); } +; + +LogicalANDExprNoBF: + BitwiseORExprNoBF + | LogicalANDExprNoBF AND BitwiseORExpr + { $$ = new LogicalAndNode($1, $3); } +; + +LogicalORExpr: + LogicalANDExpr + | LogicalORExpr OR LogicalANDExpr { $$ = new LogicalOrNode($1, $3); } +; + +LogicalORExprNoIn: + LogicalANDExprNoIn + | LogicalORExprNoIn OR LogicalANDExprNoIn + { $$ = new LogicalOrNode($1, $3); } +; + +LogicalORExprNoBF: + LogicalANDExprNoBF + | LogicalORExprNoBF OR LogicalANDExpr { $$ = new LogicalOrNode($1, $3); } +; + +ConditionalExpr: + LogicalORExpr + | LogicalORExpr '?' AssignmentExpr ':' AssignmentExpr + { $$ = new ConditionalNode($1, $3, $5); } +; + +ConditionalExprNoIn: + LogicalORExprNoIn + | LogicalORExprNoIn '?' AssignmentExprNoIn ':' AssignmentExprNoIn + { $$ = new ConditionalNode($1, $3, $5); } +; + +ConditionalExprNoBF: + LogicalORExprNoBF + | LogicalORExprNoBF '?' AssignmentExpr ':' AssignmentExpr + { $$ = new ConditionalNode($1, $3, $5); } +; + +AssignmentExpr: + ConditionalExpr + | LeftHandSideExpr AssignmentOperator AssignmentExpr + { $$ = makeAssignNode($1, $2, $3); } +; + +AssignmentExprNoIn: + ConditionalExprNoIn + | LeftHandSideExpr AssignmentOperator AssignmentExprNoIn + { $$ = makeAssignNode($1, $2, $3); } +; + +AssignmentExprNoBF: + ConditionalExprNoBF + | LeftHandSideExprNoBF AssignmentOperator AssignmentExpr + { $$ = makeAssignNode($1, $2, $3); } +; + +AssignmentOperator: + '=' { $$ = OpEqual; } + | PLUSEQUAL { $$ = OpPlusEq; } + | MINUSEQUAL { $$ = OpMinusEq; } + | MULTEQUAL { $$ = OpMultEq; } + | DIVEQUAL { $$ = OpDivEq; } + | LSHIFTEQUAL { $$ = OpLShift; } + | RSHIFTEQUAL { $$ = OpRShift; } + | URSHIFTEQUAL { $$ = OpURShift; } + | ANDEQUAL { $$ = OpAndEq; } + | XOREQUAL { $$ = OpXOrEq; } + | OREQUAL { $$ = OpOrEq; } + | MODEQUAL { $$ = OpModEq; } +; + +Expr: + AssignmentExpr + | Expr ',' AssignmentExpr { $$ = new CommaNode($1, $3); } +; + +ExprNoIn: + AssignmentExprNoIn + | ExprNoIn ',' AssignmentExprNoIn { $$ = new CommaNode($1, $3); } +; + +ExprNoBF: + AssignmentExprNoBF + | ExprNoBF ',' AssignmentExpr { $$ = new CommaNode($1, $3); } +; + +Statement: + Block + | VariableStatement + | ConstStatement + | EmptyStatement + | ExprStatement + | IfStatement + | IterationStatement + | ContinueStatement + | BreakStatement + | ReturnStatement + | WithStatement + | SwitchStatement + | LabelledStatement + | ThrowStatement + | TryStatement + | DebuggerStatement +; + +Block: + '{' '}' { $$ = createNodeInfo<StatementNode*>(new BlockNode(0), 0, 0); + DBG($$.m_node, @1, @2); } + | '{' SourceElements '}' { $$ = createNodeInfo<StatementNode*>(new BlockNode($2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @3); } +; + +VariableStatement: + VAR VariableDeclarationList ';' { $$ = createNodeInfo<StatementNode*>(makeVarStatementNode($2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @3); } + | VAR VariableDeclarationList error { $$ = createNodeInfo<StatementNode*>(makeVarStatementNode($2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @2); + AUTO_SEMICOLON; } +; + +VariableDeclarationList: + IDENT { $$.m_node = 0; + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; + appendToVarDeclarationList($$.m_varDeclarations, *$1, 0); + $$.m_funcDeclarations = 0; + } + | IDENT Initializer { $$.m_node = new AssignResolveNode(*$1, $2); + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; + appendToVarDeclarationList($$.m_varDeclarations, *$1, DeclarationStacks::HasInitializer); + $$.m_funcDeclarations = 0; + } + | VariableDeclarationList ',' IDENT + { $$.m_node = $1.m_node; + $$.m_varDeclarations = $1.m_varDeclarations; + appendToVarDeclarationList($$.m_varDeclarations, *$3, 0); + $$.m_funcDeclarations = 0; + } + | VariableDeclarationList ',' IDENT Initializer + { $$.m_node = combineVarInitializers($1.m_node, new AssignResolveNode(*$3, $4)); + $$.m_varDeclarations = $1.m_varDeclarations; + appendToVarDeclarationList($$.m_varDeclarations, *$3, DeclarationStacks::HasInitializer); + $$.m_funcDeclarations = 0; + } +; + +VariableDeclarationListNoIn: + IDENT { $$.m_node = 0; + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; + appendToVarDeclarationList($$.m_varDeclarations, *$1, 0); + $$.m_funcDeclarations = 0; + } + | IDENT InitializerNoIn { $$.m_node = new AssignResolveNode(*$1, $2); + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; + appendToVarDeclarationList($$.m_varDeclarations, *$1, DeclarationStacks::HasInitializer); + $$.m_funcDeclarations = 0; + } + | VariableDeclarationListNoIn ',' IDENT + { $$.m_node = $1.m_node; + $$.m_varDeclarations = $1.m_varDeclarations; + appendToVarDeclarationList($$.m_varDeclarations, *$3, 0); + $$.m_funcDeclarations = 0; + } + | VariableDeclarationListNoIn ',' IDENT InitializerNoIn + { $$.m_node = combineVarInitializers($1.m_node, new AssignResolveNode(*$3, $4)); + $$.m_varDeclarations = $1.m_varDeclarations; + appendToVarDeclarationList($$.m_varDeclarations, *$3, DeclarationStacks::HasInitializer); + $$.m_funcDeclarations = 0; + } +; + +ConstStatement: + CONSTTOKEN ConstDeclarationList ';' { $$ = createNodeInfo<StatementNode*>(new ConstStatementNode($2.m_node.head), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @3); } + | CONSTTOKEN ConstDeclarationList error + { $$ = createNodeInfo<StatementNode*>(new ConstStatementNode($2.m_node.head), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } +; + +ConstDeclarationList: + ConstDeclaration { $$.m_node.head = $1; + $$.m_node.tail = $$.m_node.head; + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; + appendToVarDeclarationList($$.m_varDeclarations, $1); + $$.m_funcDeclarations = 0; } + | ConstDeclarationList ',' ConstDeclaration + { $$.m_node.head = $1.m_node.head; + $1.m_node.tail->m_next = $3; + $$.m_node.tail = $3; + $$.m_varDeclarations = $1.m_varDeclarations; + appendToVarDeclarationList($$.m_varDeclarations, $3); + $$.m_funcDeclarations = 0; } +; + +ConstDeclaration: + IDENT { $$ = new ConstDeclNode(*$1, 0); } + | IDENT Initializer { $$ = new ConstDeclNode(*$1, $2); } +; + +Initializer: + '=' AssignmentExpr { $$ = $2; } +; + +InitializerNoIn: + '=' AssignmentExprNoIn { $$ = $2; } +; + +EmptyStatement: + ';' { $$ = createNodeInfo<StatementNode*>(new EmptyStatementNode(), 0, 0); } +; + +ExprStatement: + ExprNoBF ';' { $$ = createNodeInfo<StatementNode*>(new ExprStatementNode($1), 0, 0); + DBG($$.m_node, @1, @2); } + | ExprNoBF error { $$ = createNodeInfo<StatementNode*>(new ExprStatementNode($1), 0, 0); + DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } +; + +IfStatement: + IF '(' Expr ')' Statement %prec IF_WITHOUT_ELSE + { $$ = createNodeInfo<StatementNode*>(new IfNode($3, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations); + DBG($$.m_node, @1, @4); } + | IF '(' Expr ')' Statement ELSE Statement + { $$ = createNodeInfo<StatementNode*>(new IfElseNode($3, $5.m_node, $7.m_node), mergeDeclarationLists($5.m_varDeclarations, $7.m_varDeclarations), mergeDeclarationLists($5.m_funcDeclarations, $7.m_funcDeclarations)); + DBG($$.m_node, @1, @4); } +; + +IterationStatement: + DO Statement WHILE '(' Expr ')' ';' { $$ = createNodeInfo<StatementNode*>(new DoWhileNode($2.m_node, $5), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @3); } + | DO Statement WHILE '(' Expr ')' error { $$ = createNodeInfo<StatementNode*>(new DoWhileNode($2.m_node, $5), $2.m_varDeclarations, $2.m_funcDeclarations); + DBG($$.m_node, @1, @3); } // Always performs automatic semicolon insertion. + | WHILE '(' Expr ')' Statement { $$ = createNodeInfo<StatementNode*>(new WhileNode($3, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations); + DBG($$.m_node, @1, @4); } + | FOR '(' ExprNoInOpt ';' ExprOpt ';' ExprOpt ')' Statement + { $$ = createNodeInfo<StatementNode*>(new ForNode($3, $5, $7, $9.m_node, false), $9.m_varDeclarations, $9.m_funcDeclarations); + DBG($$.m_node, @1, @8); + } + | FOR '(' VAR VariableDeclarationListNoIn ';' ExprOpt ';' ExprOpt ')' Statement + { $$ = createNodeInfo<StatementNode*>(new ForNode($4.m_node, $6, $8, $10.m_node, true), + mergeDeclarationLists($4.m_varDeclarations, $10.m_varDeclarations), + mergeDeclarationLists($4.m_funcDeclarations, $10.m_funcDeclarations)); + DBG($$.m_node, @1, @9); } + | FOR '(' LeftHandSideExpr INTOKEN Expr ')' Statement + { + ExpressionNode* n = $3; + if (!n->isLocation()) + YYABORT; + $$ = createNodeInfo<StatementNode*>(new ForInNode(n, $5, $7.m_node), $7.m_varDeclarations, $7.m_funcDeclarations); + DBG($$.m_node, @1, @6); + } + | FOR '(' VAR IDENT INTOKEN Expr ')' Statement + { ForInNode *forIn = new ForInNode(*$4, 0, $6, $8.m_node); + appendToVarDeclarationList($8.m_varDeclarations, *$4, DeclarationStacks::HasInitializer); + $$ = createNodeInfo<StatementNode*>(forIn, $8.m_varDeclarations, $8.m_funcDeclarations); + DBG($$.m_node, @1, @7); } + | FOR '(' VAR IDENT InitializerNoIn INTOKEN Expr ')' Statement + { ForInNode *forIn = new ForInNode(*$4, $5, $7, $9.m_node); + appendToVarDeclarationList($9.m_varDeclarations, *$4, DeclarationStacks::HasInitializer); + $$ = createNodeInfo<StatementNode*>(forIn, $9.m_varDeclarations, $9.m_funcDeclarations); + DBG($$.m_node, @1, @8); } +; + +ExprOpt: + /* nothing */ { $$ = 0; } + | Expr +; + +ExprNoInOpt: + /* nothing */ { $$ = 0; } + | ExprNoIn +; + +ContinueStatement: + CONTINUE ';' { $$ = createNodeInfo<StatementNode*>(new ContinueNode(), 0, 0); + DBG($$.m_node, @1, @2); } + | CONTINUE error { $$ = createNodeInfo<StatementNode*>(new ContinueNode(), 0, 0); + DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } + | CONTINUE IDENT ';' { $$ = createNodeInfo<StatementNode*>(new ContinueNode(*$2), 0, 0); + DBG($$.m_node, @1, @3); } + | CONTINUE IDENT error { $$ = createNodeInfo<StatementNode*>(new ContinueNode(*$2), 0, 0); + DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } +; + +BreakStatement: + BREAK ';' { $$ = createNodeInfo<StatementNode*>(new BreakNode(), 0, 0); DBG($$.m_node, @1, @2); } + | BREAK error { $$ = createNodeInfo<StatementNode*>(new BreakNode(), 0, 0); DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } + | BREAK IDENT ';' { $$ = createNodeInfo<StatementNode*>(new BreakNode(*$2), 0, 0); DBG($$.m_node, @1, @3); } + | BREAK IDENT error { $$ = createNodeInfo<StatementNode*>(new BreakNode(*$2), 0, 0); DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } +; + +ReturnStatement: + RETURN ';' { $$ = createNodeInfo<StatementNode*>(new ReturnNode(0), 0, 0); DBG($$.m_node, @1, @2); } + | RETURN error { $$ = createNodeInfo<StatementNode*>(new ReturnNode(0), 0, 0); DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } + | RETURN Expr ';' { $$ = createNodeInfo<StatementNode*>(new ReturnNode($2), 0, 0); DBG($$.m_node, @1, @3); } + | RETURN Expr error { $$ = createNodeInfo<StatementNode*>(new ReturnNode($2), 0, 0); DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } +; + +WithStatement: + WITH '(' Expr ')' Statement { $$ = createNodeInfo<StatementNode*>(new WithNode($3, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations); + DBG($$.m_node, @1, @4); } +; + +SwitchStatement: + SWITCH '(' Expr ')' CaseBlock { $$ = createNodeInfo<StatementNode*>(new SwitchNode($3, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations); + DBG($$.m_node, @1, @4); } +; + +CaseBlock: + '{' CaseClausesOpt '}' { $$ = createNodeInfo<CaseBlockNode*>(new CaseBlockNode($2.m_node.head, 0, 0), $2.m_varDeclarations, $2.m_funcDeclarations); } + | '{' CaseClausesOpt DefaultClause CaseClausesOpt '}' + { $$ = createNodeInfo<CaseBlockNode*>(new CaseBlockNode($2.m_node.head, $3.m_node, $4.m_node.head), + mergeDeclarationLists(mergeDeclarationLists($2.m_varDeclarations, $3.m_varDeclarations), $4.m_varDeclarations), + mergeDeclarationLists(mergeDeclarationLists($2.m_funcDeclarations, $3.m_funcDeclarations), $4.m_funcDeclarations)); } +; + +CaseClausesOpt: + /* nothing */ { $$.m_node.head = 0; $$.m_node.tail = 0; $$.m_varDeclarations = 0; $$.m_funcDeclarations = 0; } + | CaseClauses +; + +CaseClauses: + CaseClause { $$.m_node.head = new ClauseListNode($1.m_node); + $$.m_node.tail = $$.m_node.head; + $$.m_varDeclarations = $1.m_varDeclarations; + $$.m_funcDeclarations = $1.m_funcDeclarations; } + | CaseClauses CaseClause { $$.m_node.head = $1.m_node.head; + $$.m_node.tail = new ClauseListNode($1.m_node.tail, $2.m_node); + $$.m_varDeclarations = mergeDeclarationLists($1.m_varDeclarations, $2.m_varDeclarations); + $$.m_funcDeclarations = mergeDeclarationLists($1.m_funcDeclarations, $2.m_funcDeclarations); + } +; + +CaseClause: + CASE Expr ':' { $$ = createNodeInfo<CaseClauseNode*>(new CaseClauseNode($2), 0, 0); } + | CASE Expr ':' SourceElements { $$ = createNodeInfo<CaseClauseNode*>(new CaseClauseNode($2, $4.m_node), $4.m_varDeclarations, $4.m_funcDeclarations); } +; + +DefaultClause: + DEFAULT ':' { $$ = createNodeInfo<CaseClauseNode*>(new CaseClauseNode(0), 0, 0); } + | DEFAULT ':' SourceElements { $$ = createNodeInfo<CaseClauseNode*>(new CaseClauseNode(0, $3.m_node), $3.m_varDeclarations, $3.m_funcDeclarations); } +; + +LabelledStatement: + IDENT ':' Statement { $3.m_node->pushLabel(*$1); + $$ = createNodeInfo<StatementNode*>(new LabelNode(*$1, $3.m_node), $3.m_varDeclarations, $3.m_funcDeclarations); } +; + +ThrowStatement: + THROW Expr ';' { $$ = createNodeInfo<StatementNode*>(new ThrowNode($2), 0, 0); DBG($$.m_node, @1, @3); } + | THROW Expr error { $$ = createNodeInfo<StatementNode*>(new ThrowNode($2), 0, 0); DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } +; + +TryStatement: + TRY Block FINALLY Block { $$ = createNodeInfo<StatementNode*>(new TryNode($2.m_node, CommonIdentifiers::shared()->nullIdentifier, 0, $4.m_node), + mergeDeclarationLists($2.m_varDeclarations, $4.m_varDeclarations), + mergeDeclarationLists($2.m_funcDeclarations, $4.m_funcDeclarations)); + DBG($$.m_node, @1, @2); } + | TRY Block CATCH '(' IDENT ')' Block { $$ = createNodeInfo<StatementNode*>(new TryNode($2.m_node, *$5, $7.m_node, 0), + mergeDeclarationLists($2.m_varDeclarations, $7.m_varDeclarations), + mergeDeclarationLists($2.m_funcDeclarations, $7.m_funcDeclarations)); + DBG($$.m_node, @1, @2); } + | TRY Block CATCH '(' IDENT ')' Block FINALLY Block + { $$ = createNodeInfo<StatementNode*>(new TryNode($2.m_node, *$5, $7.m_node, $9.m_node), + mergeDeclarationLists(mergeDeclarationLists($2.m_varDeclarations, $7.m_varDeclarations), $9.m_varDeclarations), + mergeDeclarationLists(mergeDeclarationLists($2.m_funcDeclarations, $7.m_funcDeclarations), $9.m_funcDeclarations)); + DBG($$.m_node, @1, @2); } +; + +DebuggerStatement: + DEBUGGER ';' { $$ = createNodeInfo<StatementNode*>(new EmptyStatementNode(), 0, 0); + DBG($$.m_node, @1, @2); } + | DEBUGGER error { $$ = createNodeInfo<StatementNode*>(new EmptyStatementNode(), 0, 0); + DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } +; + +FunctionDeclaration: + FUNCTION IDENT '(' ')' '{' FunctionBody '}' { $$ = new FuncDeclNode(*$2, $6); DBG($6, @5, @7); } + | FUNCTION IDENT '(' FormalParameterList ')' '{' FunctionBody '}' + { $$ = new FuncDeclNode(*$2, $4.head, $7); DBG($7, @6, @8); } +; + +FunctionExpr: + FUNCTION '(' ')' '{' FunctionBody '}' { $$ = new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, $5); DBG($5, @4, @6); } + | FUNCTION '(' FormalParameterList ')' '{' FunctionBody '}' { $$ = new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, $6, $3.head); DBG($6, @5, @7); } + | FUNCTION IDENT '(' ')' '{' FunctionBody '}' { $$ = new FuncExprNode(*$2, $6); DBG($6, @5, @7); } + | FUNCTION IDENT '(' FormalParameterList ')' '{' FunctionBody '}' { $$ = new FuncExprNode(*$2, $7, $4.head); DBG($7, @6, @8); } +; + +FormalParameterList: + IDENT { $$.head = new ParameterNode(*$1); + $$.tail = $$.head; } + | FormalParameterList ',' IDENT { $$.head = $1.head; + $$.tail = new ParameterNode($1.tail, *$3); } +; + +FunctionBody: + /* not in spec */ { $$ = FunctionBodyNode::create(0, 0, 0); } + | SourceElements { $$ = FunctionBodyNode::create($1.m_node, $1.m_varDeclarations ? &$1.m_varDeclarations->data : 0, + $1.m_funcDeclarations ? &$1.m_funcDeclarations->data : 0); + // As in mergeDeclarationLists() we have to ref/deref to safely get rid of + // the declaration lists. + if ($1.m_varDeclarations) { + $1.m_varDeclarations->ref(); + $1.m_varDeclarations->deref(); + } + if ($1.m_funcDeclarations) { + $1.m_funcDeclarations->ref(); + $1.m_funcDeclarations->deref(); + } + } +; + +Program: + /* not in spec */ { parser().didFinishParsing(0, 0, 0, @0.last_line); } + | SourceElements { parser().didFinishParsing($1.m_node, $1.m_varDeclarations, $1.m_funcDeclarations, @1.last_line); } +; + +SourceElements: + SourceElement { $$.m_node = new SourceElements; + $$.m_node->append($1.m_node); + $$.m_varDeclarations = $1.m_varDeclarations; + $$.m_funcDeclarations = $1.m_funcDeclarations; + } + | SourceElements SourceElement { $$.m_node->append($2.m_node); + $$.m_varDeclarations = mergeDeclarationLists($1.m_varDeclarations, $2.m_varDeclarations); + $$.m_funcDeclarations = mergeDeclarationLists($1.m_funcDeclarations, $2.m_funcDeclarations); + } +; + +SourceElement: + FunctionDeclaration { $$ = createNodeInfo<StatementNode*>($1, 0, new ParserRefCountedData<DeclarationStacks::FunctionStack>); $$.m_funcDeclarations->data.append($1); } + | Statement { $$ = $1; } +; + +%% + +static AddNode* makeAddNode(ExpressionNode* left, ExpressionNode* right) +{ + JSType t1 = left->expectedReturnType(); + JSType t2 = right->expectedReturnType(); + + if (t1 == NumberType && t2 == NumberType) + return new AddNumbersNode(left, right); + if (t1 == StringType && t2 == StringType) + return new AddStringsNode(left, right); + if (t1 == StringType) + return new AddStringLeftNode(left, right); + if (t2 == StringType) + return new AddStringRightNode(left, right); + return new AddNode(left, right); +} + +static LessNode* makeLessNode(ExpressionNode* left, ExpressionNode* right) +{ + JSType t1 = left->expectedReturnType(); + JSType t2 = right->expectedReturnType(); + + if (t1 == StringType && t2 == StringType) + return new LessStringsNode(left, right); + + // There are certainly more efficient ways to do this type check if necessary + if (t1 == NumberType || t1 == BooleanType || t1 == UndefinedType || t1 == NullType || + t2 == NumberType || t2 == BooleanType || t2 == UndefinedType || t2 == NullType) + return new LessNumbersNode(left, right); + + // Neither is certain to be a number, nor were both certain to be strings, so we use the default (slow) implementation. + return new LessNode(left, right); +} + +static ExpressionNode* makeAssignNode(ExpressionNode* loc, Operator op, ExpressionNode* expr) +{ + if (!loc->isLocation()) + return new AssignErrorNode(loc, op, expr); + + if (loc->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(loc); + if (op == OpEqual) + return new AssignResolveNode(resolve->identifier(), expr); + else + return new ReadModifyResolveNode(resolve->identifier(), op, expr); + } + if (loc->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(loc); + if (op == OpEqual) + return new AssignBracketNode(bracket->base(), bracket->subscript(), expr); + else + return new ReadModifyBracketNode(bracket->base(), bracket->subscript(), op, expr); + } + ASSERT(loc->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(loc); + if (op == OpEqual) + return new AssignDotNode(dot->base(), dot->identifier(), expr); + return new ReadModifyDotNode(dot->base(), dot->identifier(), op, expr); +} + +static ExpressionNode* makePrefixNode(ExpressionNode* expr, Operator op) +{ + if (!expr->isLocation()) + return new PrefixErrorNode(expr, op); + + if (expr->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(expr); + if (op == OpPlusPlus) + return new PreIncResolveNode(resolve->identifier()); + else + return new PreDecResolveNode(resolve->identifier()); + } + if (expr->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr); + if (op == OpPlusPlus) + return new PreIncBracketNode(bracket->base(), bracket->subscript()); + else + return new PreDecBracketNode(bracket->base(), bracket->subscript()); + } + ASSERT(expr->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr); + if (op == OpPlusPlus) + return new PreIncDotNode(dot->base(), dot->identifier()); + return new PreDecDotNode(dot->base(), dot->identifier()); +} + +static ExpressionNode* makePostfixNode(ExpressionNode* expr, Operator op) +{ + if (!expr->isLocation()) + return new PostfixErrorNode(expr, op); + + if (expr->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(expr); + if (op == OpPlusPlus) + return new PostIncResolveNode(resolve->identifier()); + else + return new PostDecResolveNode(resolve->identifier()); + } + if (expr->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr); + if (op == OpPlusPlus) + return new PostIncBracketNode(bracket->base(), bracket->subscript()); + else + return new PostDecBracketNode(bracket->base(), bracket->subscript()); + } + ASSERT(expr->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr); + + if (op == OpPlusPlus) + return new PostIncDotNode(dot->base(), dot->identifier()); + return new PostDecDotNode(dot->base(), dot->identifier()); +} + +static ExpressionNode* makeFunctionCallNode(ExpressionNode* func, ArgumentsNode* args) +{ + if (!func->isLocation()) + return new FunctionCallValueNode(func, args); + if (func->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(func); + return new FunctionCallResolveNode(resolve->identifier(), args); + } + if (func->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(func); + return new FunctionCallBracketNode(bracket->base(), bracket->subscript(), args); + } + ASSERT(func->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(func); + return new FunctionCallDotNode(dot->base(), dot->identifier(), args); +} + +static ExpressionNode* makeTypeOfNode(ExpressionNode* expr) +{ + if (expr->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(expr); + return new TypeOfResolveNode(resolve->identifier()); + } + return new TypeOfValueNode(expr); +} + +static ExpressionNode* makeDeleteNode(ExpressionNode* expr) +{ + if (!expr->isLocation()) + return new DeleteValueNode(expr); + if (expr->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(expr); + return new DeleteResolveNode(resolve->identifier()); + } + if (expr->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr); + return new DeleteBracketNode(bracket->base(), bracket->subscript()); + } + ASSERT(expr->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr); + return new DeleteDotNode(dot->base(), dot->identifier()); +} + +static PropertyNode* makeGetterOrSetterPropertyNode(const Identifier& getOrSet, const Identifier& name, ParameterNode* params, FunctionBodyNode* body) +{ + PropertyNode::Type type; + if (getOrSet == "get") + type = PropertyNode::Getter; + else if (getOrSet == "set") + type = PropertyNode::Setter; + else + return 0; + return new PropertyNode(name, new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, body, params), type); +} + +static ExpressionNode* makeNegateNode(ExpressionNode* n) +{ + if (n->isNumber()) { + NumberNode* number = static_cast<NumberNode*>(n); + + if (number->value() > 0.0) { + number->setValue(-number->value()); + return number; + } + } + + return new NegateNode(n); +} + +static NumberNode* makeNumberNode(double d) +{ + JSValue* value = JSImmediate::from(d); + if (value) + return new ImmediateNumberNode(value, d); + return new NumberNode(d); +} + +/* called by yyparse on error */ +int yyerror(const char *) +{ + return 1; +} + +/* may we automatically insert a semicolon ? */ +static bool allowAutomaticSemicolon() +{ + return yychar == '}' || yychar == 0 || lexer().prevTerminator(); +} + +static ExpressionNode* combineVarInitializers(ExpressionNode* list, AssignResolveNode* init) +{ + if (!list) + return init; + return new VarDeclCommaNode(list, init); +} + +// We turn variable declarations into either assignments or empty +// statements (which later get stripped out), because the actual +// declaration work is hoisted up to the start of the function body +static StatementNode* makeVarStatementNode(ExpressionNode* expr) +{ + if (!expr) + return new EmptyStatementNode(); + return new VarStatementNode(expr); +} + diff --git a/JavaScriptCore/kjs/identifier.cpp b/JavaScriptCore/kjs/identifier.cpp new file mode 100644 index 0000000..3fa01ad --- /dev/null +++ b/JavaScriptCore/kjs/identifier.cpp @@ -0,0 +1,205 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Apple Computer, 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 "identifier.h" + +#include "JSLock.h" +#include <new> // for placement new +#include <string.h> // for strlen +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/HashSet.h> + +namespace WTF { + + template<typename T> struct DefaultHash; + template<typename T> struct StrHash; + + template<> struct StrHash<KJS::UString::Rep *> { + static unsigned hash(const KJS::UString::Rep *key) { return key->hash(); } + static bool equal(const KJS::UString::Rep *a, const KJS::UString::Rep *b) { return KJS::Identifier::equal(a, b); } + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + template<> struct DefaultHash<KJS::UString::Rep *> { + typedef StrHash<KJS::UString::Rep *> Hash; + }; + +} + +namespace KJS { + +typedef HashSet<UString::Rep *> IdentifierTable; +static IdentifierTable *table; + +static inline IdentifierTable& identifierTable() +{ + ASSERT(JSLock::lockCount() > 0); + + if (!table) + table = new IdentifierTable; + return *table; +} + + +bool Identifier::equal(const UString::Rep *r, const char *s) +{ + int length = r->len; + const UChar *d = r->data(); + for (int i = 0; i != length; ++i) + if (d[i].uc != (unsigned char)s[i]) + return false; + return s[length] == 0; +} + +bool Identifier::equal(const UString::Rep *r, const UChar *s, int length) +{ + if (r->len != length) + return false; + const UChar *d = r->data(); + for (int i = 0; i != length; ++i) + if (d[i].uc != s[i].uc) + return false; + return true; +} + +bool Identifier::equal(const UString::Rep *r, const UString::Rep *b) +{ + int length = r->len; + if (length != b->len) + return false; + const UChar *d = r->data(); + const UChar *s = b->data(); + for (int i = 0; i != length; ++i) + if (d[i].uc != s[i].uc) + return false; + return true; +} + +struct CStringTranslator +{ + static unsigned hash(const char *c) + { + return UString::Rep::computeHash(c); + } + + static bool equal(UString::Rep *r, const char *s) + { + return Identifier::equal(r, s); + } + + static void translate(UString::Rep*& location, const char *c, unsigned hash) + { + size_t length = strlen(c); + UChar *d = static_cast<UChar *>(fastMalloc(sizeof(UChar) * length)); + for (size_t i = 0; i != length; i++) + d[i] = c[i]; + + UString::Rep *r = UString::Rep::create(d, static_cast<int>(length)).releaseRef(); + r->isIdentifier = 1; + r->rc = 0; + r->_hash = hash; + + location = r; + } +}; + +PassRefPtr<UString::Rep> Identifier::add(const char *c) +{ + if (!c) { + UString::Rep::null.hash(); + return &UString::Rep::null; + } + + if (!c[0]) { + UString::Rep::empty.hash(); + return &UString::Rep::empty; + } + + return *identifierTable().add<const char *, CStringTranslator>(c).first; +} + +struct UCharBuffer { + const UChar *s; + unsigned int length; +}; + +struct UCharBufferTranslator +{ + static unsigned hash(const UCharBuffer& buf) + { + return UString::Rep::computeHash(buf.s, buf.length); + } + + static bool equal(UString::Rep *str, const UCharBuffer& buf) + { + return Identifier::equal(str, buf.s, buf.length); + } + + static void translate(UString::Rep *& location, const UCharBuffer& buf, unsigned hash) + { + UChar *d = static_cast<UChar *>(fastMalloc(sizeof(UChar) * buf.length)); + for (unsigned i = 0; i != buf.length; i++) + d[i] = buf.s[i]; + + UString::Rep *r = UString::Rep::create(d, buf.length).releaseRef(); + r->isIdentifier = 1; + r->rc = 0; + r->_hash = hash; + + location = r; + } +}; + +PassRefPtr<UString::Rep> Identifier::add(const UChar *s, int length) +{ + if (!length) { + UString::Rep::empty.hash(); + return &UString::Rep::empty; + } + + UCharBuffer buf = {s, length}; + return *identifierTable().add<UCharBuffer, UCharBufferTranslator>(buf).first; +} + +PassRefPtr<UString::Rep> Identifier::addSlowCase(UString::Rep *r) +{ + ASSERT(!r->isIdentifier); + + if (r->len == 0) { + UString::Rep::empty.hash(); + return &UString::Rep::empty; + } + + UString::Rep *result = *identifierTable().add(r).first; + if (result == r) + r->isIdentifier = true; + return result; +} + +void Identifier::remove(UString::Rep *r) +{ + identifierTable().remove(r); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/identifier.h b/JavaScriptCore/kjs/identifier.h new file mode 100644 index 0000000..fc43344 --- /dev/null +++ b/JavaScriptCore/kjs/identifier.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2003, 2006, 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 KJS_IDENTIFIER_H +#define KJS_IDENTIFIER_H + +#include "ustring.h" + +namespace KJS { + + class Identifier { + friend class PropertyMap; + public: + Identifier() { } + Identifier(const char* s) : _ustring(add(s)) { } + Identifier(const UChar* s, int length) : _ustring(add(s, length)) { } + explicit Identifier(UString::Rep* rep) : _ustring(add(rep)) { } + explicit Identifier(const UString& s) : _ustring(add(s.rep())) { } + + // Special constructor for cases where we overwrite an object in place. + Identifier(PlacementNewAdoptType) : _ustring(PlacementNewAdopt) { } + + const UString& ustring() const { return _ustring; } + DOM::DOMString domString() const; + + const UChar* data() const { return _ustring.data(); } + int size() const { return _ustring.size(); } + + const char* ascii() const { return _ustring.ascii(); } + + static Identifier from(unsigned y) { return Identifier(UString::from(y)); } + + bool isNull() const { return _ustring.isNull(); } + bool isEmpty() const { return _ustring.isEmpty(); } + + uint32_t toUInt32(bool* ok) const { return _ustring.toUInt32(ok); } + uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const { return _ustring.toUInt32(ok, tolerateEmptyString); }; + uint32_t toStrictUInt32(bool* ok) const { return _ustring.toStrictUInt32(ok); } + unsigned toArrayIndex(bool* ok) const { return _ustring.toArrayIndex(ok); } + double toDouble() const { return _ustring.toDouble(); } + + friend bool operator==(const Identifier&, const Identifier&); + friend bool operator!=(const Identifier&, const Identifier&); + + friend bool operator==(const Identifier&, const char*); + + static void remove(UString::Rep* ); + + static bool equal(const UString::Rep*, const char*); + static bool equal(const UString::Rep*, const UChar*, int length); + static bool equal(const UString::Rep*, const UString::Rep*); + + private: + UString _ustring; + + static bool equal(const Identifier& a, const Identifier& b) + { return a._ustring.rep() == b._ustring.rep(); } + static bool equal(const Identifier& a, const char* b) + { return equal(a._ustring.rep(), b); } + + static PassRefPtr<UString::Rep> add(const char*); + static PassRefPtr<UString::Rep> add(const UChar*, int length); + static PassRefPtr<UString::Rep> add(UString::Rep* r) + { + if (r->isIdentifier) + return r; + return addSlowCase(r); + } + static PassRefPtr<UString::Rep> addSlowCase(UString::Rep *r); + }; + + inline bool operator==(const Identifier& a, const Identifier& b) + { return Identifier::equal(a, b); } + + inline bool operator!=(const Identifier& a, const Identifier& b) + { return !Identifier::equal(a, b); } + + inline bool operator==(const Identifier& a, const char* b) + { return Identifier::equal(a, b); } + +} // namespace KJS + +#endif // KJS_IDENTIFIER_H diff --git a/JavaScriptCore/kjs/internal.cpp b/JavaScriptCore/kjs/internal.cpp new file mode 100644 index 0000000..5482b48 --- /dev/null +++ b/JavaScriptCore/kjs/internal.cpp @@ -0,0 +1,302 @@ +/* + * 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 "internal.h" + +#include "ExecState.h" +#include "array_object.h" +#include "bool_object.h" +#include "collector.h" +#include "date_object.h" +#include "debugger.h" +#include "error_object.h" +#include "function_object.h" +#include "lexer.h" +#include "math_object.h" +#include "nodes.h" +#include "number_object.h" +#include "object.h" +#include "object_object.h" +#include "operations.h" +#include "regexp_object.h" +#include "string_object.h" +#include <math.h> +#include <stdio.h> +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +namespace KJS { + +// ------------------------------ StringImp ------------------------------------ + +JSValue* StringImp::toPrimitive(ExecState*, JSType) const +{ + return const_cast<StringImp*>(this); +} + +bool StringImp::getPrimitiveNumber(ExecState*, double& number, JSValue*& value) +{ + value = this; + number = val.toDouble(); + return false; +} + +bool StringImp::toBoolean(ExecState *) const +{ + return (val.size() > 0); +} + +double StringImp::toNumber(ExecState *) const +{ + return val.toDouble(); +} + +UString StringImp::toString(ExecState *) const +{ + return val; +} + +JSObject* StringImp::toObject(ExecState *exec) const +{ + return new StringInstance(exec->lexicalGlobalObject()->stringPrototype(), const_cast<StringImp*>(this)); +} + +// ------------------------------ NumberImp ------------------------------------ + +JSValue* NumberImp::toPrimitive(ExecState*, JSType) const +{ + return const_cast<NumberImp*>(this); +} + +bool NumberImp::getPrimitiveNumber(ExecState*, double& number, JSValue*& value) +{ + number = val; + value = this; + return true; +} + +bool NumberImp::toBoolean(ExecState *) const +{ + return val < 0.0 || val > 0.0; // false for NaN +} + +double NumberImp::toNumber(ExecState *) const +{ + return val; +} + +UString NumberImp::toString(ExecState *) const +{ + if (val == 0.0) // +0.0 or -0.0 + return "0"; + return UString::from(val); +} + +JSObject *NumberImp::toObject(ExecState *exec) const +{ + List args; + args.append(const_cast<NumberImp*>(this)); + return static_cast<JSObject *>(exec->lexicalGlobalObject()->numberConstructor()->construct(exec,args)); +} + +bool NumberImp::getUInt32(uint32_t& uint32) const +{ + uint32 = static_cast<uint32_t>(val); + return uint32 == val; +} + +bool NumberImp::getTruncatedInt32(int32_t& int32) const +{ + if (!(val >= -2147483648.0 && val < 2147483648.0)) + return false; + int32 = static_cast<int32_t>(val); + return true; +} + +bool NumberImp::getTruncatedUInt32(uint32_t& uint32) const +{ + if (!(val >= 0.0 && val < 4294967296.0)) + return false; + uint32 = static_cast<uint32_t>(val); + return true; +} + +// --------------------------- GetterSetterImp --------------------------------- +void GetterSetterImp::mark() +{ + JSCell::mark(); + + if (getter && !getter->marked()) + getter->mark(); + if (setter && !setter->marked()) + setter->mark(); +} + +JSValue* GetterSetterImp::toPrimitive(ExecState*, JSType) const +{ + ASSERT(false); + return jsNull(); +} + +bool GetterSetterImp::getPrimitiveNumber(ExecState*, double& number, JSValue*& value) +{ + ASSERT_NOT_REACHED(); + number = 0; + value = 0; + return true; +} + +bool GetterSetterImp::toBoolean(ExecState*) const +{ + ASSERT(false); + return false; +} + +double GetterSetterImp::toNumber(ExecState *) const +{ + ASSERT(false); + return 0.0; +} + +UString GetterSetterImp::toString(ExecState *) const +{ + ASSERT(false); + return UString::null(); +} + +JSObject *GetterSetterImp::toObject(ExecState *exec) const +{ + ASSERT(false); + return jsNull()->toObject(exec); +} + +// ------------------------------ LabelStack ----------------------------------- + +bool LabelStack::push(const Identifier &id) +{ + if (contains(id)) + return false; + + StackElem *newtos = new StackElem; + newtos->id = id; + newtos->prev = tos; + tos = newtos; + return true; +} + +bool LabelStack::contains(const Identifier &id) const +{ + if (id.isEmpty()) + return true; + + for (StackElem *curr = tos; curr; curr = curr->prev) + if (curr->id == id) + return true; + + return false; +} + +// ------------------------------ InternalFunctionImp -------------------------- + +const ClassInfo InternalFunctionImp::info = { "Function", 0, 0 }; + +InternalFunctionImp::InternalFunctionImp() +{ +} + +InternalFunctionImp::InternalFunctionImp(FunctionPrototype* funcProto, const Identifier& name) + : JSObject(funcProto) + , m_name(name) +{ +} + +bool InternalFunctionImp::implementsCall() const +{ + return true; +} + +bool InternalFunctionImp::implementsHasInstance() const +{ + return true; +} + +// ------------------------------ global functions ----------------------------- + +#ifndef NDEBUG +#include <stdio.h> +void printInfo(ExecState *exec, const char *s, JSValue *o, int lineno) +{ + if (!o) + fprintf(stderr, "KJS: %s: (null)", s); + else { + JSValue *v = o; + + UString name; + switch (v->type()) { + case UnspecifiedType: + name = "Unspecified"; + break; + case UndefinedType: + name = "Undefined"; + break; + case NullType: + name = "Null"; + break; + case BooleanType: + name = "Boolean"; + break; + case StringType: + name = "String"; + break; + case NumberType: + name = "Number"; + break; + case ObjectType: + name = static_cast<JSObject *>(v)->className(); + if (name.isNull()) + name = "(unknown class)"; + break; + case GetterSetterType: + name = "GetterSetter"; + break; + } + UString vString = v->toString(exec); + if ( vString.size() > 50 ) + vString = vString.substr( 0, 50 ) + "..."; + // Can't use two UString::ascii() in the same fprintf call + CString tempString( vString.cstring() ); + + fprintf(stderr, "KJS: %s: %s : %s (%p)", + s, tempString.c_str(), name.ascii(), (void*)v); + + if (lineno >= 0) + fprintf(stderr, ", line %d\n",lineno); + else + fprintf(stderr, "\n"); + } +} +#endif + +} diff --git a/JavaScriptCore/kjs/internal.h b/JavaScriptCore/kjs/internal.h new file mode 100644 index 0000000..d3a2eb2 --- /dev/null +++ b/JavaScriptCore/kjs/internal.h @@ -0,0 +1,117 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 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 INTERNAL_H +#define INTERNAL_H + +#include "JSType.h" +#include "object.h" +#include "protect.h" +#include "scope_chain.h" +#include "types.h" +#include "ustring.h" + +#include <wtf/Noncopyable.h> + +#define I18N_NOOP(s) s + +namespace KJS { + + class FunctionPrototype; + + // --------------------------------------------------------------------------- + // Primitive impls + // --------------------------------------------------------------------------- + + class StringImp : public JSCell { + public: + StringImp(const UString& v) : val(v) { Collector::reportExtraMemoryCost(v.cost()); } + enum HasOtherOwnerType { HasOtherOwner }; + StringImp(const UString& value, HasOtherOwnerType) : val(value) { } + const UString& value() const { return val; } + + private: + virtual JSType type() const { return StringType; } + + virtual JSValue* toPrimitive(ExecState*, JSType preferred = UnspecifiedType) const; + virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value); + virtual bool toBoolean(ExecState *exec) const; + virtual double toNumber(ExecState *exec) const; + virtual JSObject *toObject(ExecState *exec) const; + virtual UString toString(ExecState*) const; + + UString val; + }; + + class NumberImp : public JSCell { + friend class ConstantValues; + friend JSValue *jsNumberCell(double); + public: + double value() const { return val; } + + virtual JSType type() const { return NumberType; } + + virtual JSValue* toPrimitive(ExecState*, JSType preferred = UnspecifiedType) const; + virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value); + virtual bool toBoolean(ExecState *exec) const; + virtual double toNumber(ExecState *exec) const; + virtual UString toString(ExecState *exec) const; + virtual JSObject *toObject(ExecState *exec) const; + + void* operator new(size_t size) + { + return Collector::allocateNumber(size); + } + private: + NumberImp(double v) : val(v) { } + + virtual bool getUInt32(uint32_t&) const; + virtual bool getTruncatedInt32(int32_t&) const; + virtual bool getTruncatedUInt32(uint32_t&) const; + + double val; + }; + + + // --------------------------------------------------------------------------- + // Evaluation + // --------------------------------------------------------------------------- + + struct AttachedGlobalObject; + class DebuggerImp { + public: + + DebuggerImp() { + globalObjects = 0; + } + + AttachedGlobalObject* globalObjects; + }; + +#ifndef NDEBUG + void printInfo(ExecState *exec, const char *s, JSValue *, int lineno = -1); +#endif + +} // namespace + +#endif // INTERNAL_H diff --git a/JavaScriptCore/kjs/interpreter.cpp b/JavaScriptCore/kjs/interpreter.cpp new file mode 100644 index 0000000..a15b6d3 --- /dev/null +++ b/JavaScriptCore/kjs/interpreter.cpp @@ -0,0 +1,159 @@ +/* + * 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 "interpreter.h" + +#include "ExecState.h" +#include "JSGlobalObject.h" +#include "Parser.h" +#include "SavedBuiltins.h" +#include "array_object.h" +#include "bool_object.h" +#include "collector.h" +#include "date_object.h" +#include "debugger.h" +#include "error_object.h" +#include "function_object.h" +#include "internal.h" +#include "math_object.h" +#include "nodes.h" +#include "number_object.h" +#include "object.h" +#include "object_object.h" +#include "operations.h" +#include "regexp_object.h" +#include "runtime.h" +#include "string_object.h" +#include "types.h" +#include "value.h" +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <wtf/Assertions.h> + +#if !PLATFORM(WIN_OS) +#include <unistd.h> +#endif + +namespace KJS { + +Completion Interpreter::checkSyntax(ExecState* exec, const UString& sourceURL, int startingLineNumber, const UString& code) +{ + return checkSyntax(exec, sourceURL, startingLineNumber, code.data(), code.size()); +} + +Completion Interpreter::checkSyntax(ExecState* exec, const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength) +{ + JSLock lock; + + int errLine; + UString errMsg; + RefPtr<ProgramNode> progNode = parser().parse<ProgramNode>(sourceURL, startingLineNumber, code, codeLength, 0, &errLine, &errMsg); + if (!progNode) + return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, 0, sourceURL)); + return Completion(Normal); +} + +Completion Interpreter::evaluate(ExecState* exec, const UString& sourceURL, int startingLineNumber, const UString& code, JSValue* thisV) +{ + return evaluate(exec, sourceURL, startingLineNumber, code.data(), code.size(), thisV); +} + +Completion Interpreter::evaluate(ExecState* exec, const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength, JSValue* thisV) +{ + JSLock lock; + + JSGlobalObject* globalObject = exec->dynamicGlobalObject(); + + if (globalObject->recursion() >= 20) + return Completion(Throw, Error::create(exec, GeneralError, "Recursion too deep")); + + // parse the source code + int sourceId; + int errLine; + UString errMsg; + RefPtr<ProgramNode> progNode = parser().parse<ProgramNode>(sourceURL, startingLineNumber, code, codeLength, &sourceId, &errLine, &errMsg); + + // notify debugger that source has been parsed + if (globalObject->debugger()) { + bool cont = globalObject->debugger()->sourceParsed(exec, sourceId, sourceURL, UString(code, codeLength), startingLineNumber, errLine, errMsg); + if (!cont) + return Completion(Break); + } + + // no program node means a syntax error occurred + if (!progNode) + return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL)); + + exec->clearException(); + + globalObject->incRecursion(); + + JSObject* thisObj = globalObject; + + // "this" must be an object... use same rules as Function.prototype.apply() + if (thisV && !thisV->isUndefinedOrNull()) + thisObj = thisV->toObject(exec); + + Completion res; + if (exec->hadException()) + // the thisV->toObject() conversion above might have thrown an exception - if so, propagate it + res = Completion(Throw, exec->exception()); + else { + // execute the code + InterpreterExecState newExec(globalObject, thisObj, progNode.get()); + JSValue* value = progNode->execute(&newExec); + res = Completion(newExec.completionType(), value); + } + + globalObject->decRecursion(); + + if (shouldPrintExceptions() && res.complType() == Throw) { + JSLock lock; + ExecState* exec = globalObject->globalExec(); + CString f = sourceURL.UTF8String(); + CString message = res.value()->toObject(exec)->toString(exec).UTF8String(); + int line = res.value()->toObject(exec)->get(exec, "line")->toUInt32(exec); +#if PLATFORM(WIN_OS) + printf("%s line %d: %s\n", f.c_str(), line, message.c_str()); +#else + printf("[%d] %s line %d: %s\n", getpid(), f.c_str(), line, message.c_str()); +#endif + } + + return res; +} + +static bool printExceptions = false; + +bool Interpreter::shouldPrintExceptions() +{ + return printExceptions; +} + +void Interpreter::setShouldPrintExceptions(bool print) +{ + printExceptions = print; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/interpreter.h b/JavaScriptCore/kjs/interpreter.h new file mode 100644 index 0000000..79b9398 --- /dev/null +++ b/JavaScriptCore/kjs/interpreter.h @@ -0,0 +1,71 @@ +/* + * 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 KJS_Interpreter_h +#define KJS_Interpreter_h + +namespace KJS { + + class Completion; + class ExecState; + class JSValue; + class UString; + + struct UChar; + + class Interpreter { + public: + /** + * Parses the supplied ECMAScript code and checks for syntax errors. + * + * @param code The code to check + * @return A normal completion if there were no syntax errors in the code, + * otherwise a throw completion with the syntax error as its value. + */ + static Completion checkSyntax(ExecState*, const UString& sourceURL, int startingLineNumber, const UString& code); + static Completion checkSyntax(ExecState*, const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength); + + /** + * Evaluates the supplied ECMAScript code. + * + * Since this method returns a Completion, you should check the type of + * completion to detect an error or before attempting to access the returned + * value. For example, if an error occurs during script execution and is not + * caught by the script, the completion type will be Throw. + * + * If the supplied code is invalid, a SyntaxError will be thrown. + * + * @param code The code to evaluate + * @param thisV The value to pass in as the "this" value for the script + * execution. This should either be jsNull() or an Object. + * @return A completion object representing the result of the execution. + */ + static Completion evaluate(ExecState*, const UString& sourceURL, int startingLineNumber, const UString& code, JSValue* thisV = 0); + static Completion evaluate(ExecState*, const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength, JSValue* thisV = 0); + + static bool shouldPrintExceptions(); + static void setShouldPrintExceptions(bool); + }; + +} // namespace KJS + +#endif // KJS_Interpreter_h diff --git a/JavaScriptCore/kjs/keywords.table b/JavaScriptCore/kjs/keywords.table new file mode 100644 index 0000000..490c1cc --- /dev/null +++ b/JavaScriptCore/kjs/keywords.table @@ -0,0 +1,72 @@ +# main keywords +@begin mainTable 41 + +# types +null NULLTOKEN +true TRUETOKEN +false FALSETOKEN + +# keywords +break BREAK +case CASE +catch CATCH +const CONSTTOKEN +default DEFAULT +finally FINALLY +for FOR +instanceof INSTANCEOF +new NEW +var VAR +continue CONTINUE +function FUNCTION +return RETURN +void VOIDTOKEN +delete DELETETOKEN +if IF +this THISTOKEN +do DO +while WHILE +else ELSE +in INTOKEN +switch SWITCH +throw THROW +try TRY +typeof TYPEOF +with WITH +debugger DEBUGGER + +# reserved for future use +class RESERVED +enum RESERVED +export RESERVED +extends RESERVED +import RESERVED +super RESERVED + +# these words are reserved for future use in the ECMA spec, but not in WinIE +# (see http://bugs.webkit.org/show_bug.cgi?id=6179) +# abstract RESERVED +# boolean RESERVED +# byte RESERVED +# char RESERVED +# double RESERVED +# final RESERVED +# float RESERVED +# goto RESERVED +# implements RESERVED +# int RESERVED +# interface RESERVED +# long RESERVED +# native RESERVED +# package RESERVED +# private RESERVED +# protected RESERVED +# public RESERVED +# short RESERVED +# static RESERVED +# synchronized RESERVED +# throws RESERVED +# transient RESERVED +# volatile RESERVED +@end + diff --git a/JavaScriptCore/kjs/lexer.cpp b/JavaScriptCore/kjs/lexer.cpp new file mode 100644 index 0000000..c4d327f --- /dev/null +++ b/JavaScriptCore/kjs/lexer.cpp @@ -0,0 +1,897 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All Rights Reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * + * 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 "lexer.h" + +#include "dtoa.h" +#include "function.h" +#include "nodes.h" +#include "NodeInfo.h" +#include <ctype.h> +#include <limits.h> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/unicode/Unicode.h> + +using namespace WTF; +using namespace Unicode; + +// we can't specify the namespace in yacc's C output, so do it here +using namespace KJS; + +#ifndef KDE_USE_FINAL +#include "grammar.h" +#endif + +#include "lookup.h" +#include "lexer.lut.h" + +extern YYLTYPE kjsyylloc; // global bison variable holding token info + +// a bridge for yacc from the C world to C++ +int kjsyylex() +{ + return lexer().lex(); +} + +namespace KJS { + +static bool isDecimalDigit(int); + +static const size_t initialReadBufferCapacity = 32; +static const size_t initialStringTableCapacity = 64; + +Lexer& lexer() +{ + ASSERT(JSLock::currentThreadIsHoldingLock()); + + // FIXME: We'd like to avoid calling new here, but we don't currently + // support tearing down the Lexer at app quit time, since that would involve + // tearing down its UString data members without holding the JSLock. + static Lexer* staticLexer = new Lexer; + return *staticLexer; +} + +Lexer::Lexer() + : yylineno(1) + , restrKeyword(false) + , eatNextIdentifier(false) + , stackToken(-1) + , lastToken(-1) + , pos(0) + , code(0) + , length(0) + , atLineStart(true) + , current(0) + , next1(0) + , next2(0) + , next3(0) +{ + m_buffer8.reserveCapacity(initialReadBufferCapacity); + m_buffer16.reserveCapacity(initialReadBufferCapacity); + m_strings.reserveCapacity(initialStringTableCapacity); + m_identifiers.reserveCapacity(initialStringTableCapacity); +} + +void Lexer::setCode(int startingLineNumber, const KJS::UChar *c, unsigned int len) +{ + yylineno = 1 + startingLineNumber; + restrKeyword = false; + delimited = false; + eatNextIdentifier = false; + stackToken = -1; + lastToken = -1; + pos = 0; + code = c; + length = len; + skipLF = false; + skipCR = false; + error = false; + atLineStart = true; + + // read first characters + current = (length > 0) ? code[0].uc : -1; + next1 = (length > 1) ? code[1].uc : -1; + next2 = (length > 2) ? code[2].uc : -1; + next3 = (length > 3) ? code[3].uc : -1; +} + +void Lexer::shift(unsigned int p) +{ + // Here would be a good place to strip Cf characters, but that has caused compatibility problems: + // <http://bugs.webkit.org/show_bug.cgi?id=10183>. + while (p--) { + pos++; + current = next1; + next1 = next2; + next2 = next3; + next3 = (pos + 3 < length) ? code[pos + 3].uc : -1; + } +} + +// called on each new line +void Lexer::nextLine() +{ + yylineno++; + atLineStart = true; +} + +void Lexer::setDone(State s) +{ + state = s; + done = true; +} + +int Lexer::lex() +{ + int token = 0; + state = Start; + unsigned short stringType = 0; // either single or double quotes + m_buffer8.clear(); + m_buffer16.clear(); + done = false; + terminator = false; + skipLF = false; + skipCR = false; + + // did we push a token on the stack previously ? + // (after an automatic semicolon insertion) + if (stackToken >= 0) { + setDone(Other); + token = stackToken; + stackToken = 0; + } + + while (!done) { + if (skipLF && current != '\n') // found \r but not \n afterwards + skipLF = false; + if (skipCR && current != '\r') // found \n but not \r afterwards + skipCR = false; + if (skipLF || skipCR) // found \r\n or \n\r -> eat the second one + { + skipLF = false; + skipCR = false; + shift(1); + } + switch (state) { + case Start: + if (isWhiteSpace()) { + // do nothing + } else if (current == '/' && next1 == '/') { + shift(1); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + shift(1); + state = InMultiLineComment; + } else if (current == -1) { + if (!terminator && !delimited) { + // automatic semicolon insertion if program incomplete + token = ';'; + stackToken = 0; + setDone(Other); + } else + setDone(Eof); + } else if (isLineTerminator()) { + nextLine(); + terminator = true; + if (restrKeyword) { + token = ';'; + setDone(Other); + } + } else if (current == '"' || current == '\'') { + state = InString; + stringType = static_cast<unsigned short>(current); + } else if (isIdentStart(current)) { + record16(current); + state = InIdentifierOrKeyword; + } else if (current == '\\') { + state = InIdentifierStartUnicodeEscapeStart; + } else if (current == '0') { + record8(current); + state = InNum0; + } else if (isDecimalDigit(current)) { + record8(current); + state = InNum; + } else if (current == '.' && isDecimalDigit(next1)) { + record8(current); + state = InDecimal; + // <!-- marks the beginning of a line comment (for www usage) + } else if (current == '<' && next1 == '!' && + next2 == '-' && next3 == '-') { + shift(3); + state = InSingleLineComment; + // same for --> + } else if (atLineStart && current == '-' && next1 == '-' && next2 == '>') { + shift(2); + state = InSingleLineComment; + } else { + token = matchPunctuator(current, next1, next2, next3); + if (token != -1) { + setDone(Other); + } else { + // cerr << "encountered unknown character" << endl; + setDone(Bad); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (isLineTerminator() || current == -1) { + setDone(Bad); + } else if (current == '\\') { + state = InEscapeSequence; + } else { + record16(current); + } + break; + // Escape Sequences inside of strings + case InEscapeSequence: + if (isOctalDigit(current)) { + if (current >= '0' && current <= '3' && + isOctalDigit(next1) && isOctalDigit(next2)) { + record16(convertOctal(current, next1, next2)); + shift(2); + state = InString; + } else if (isOctalDigit(current) && isOctalDigit(next1)) { + record16(convertOctal('0', current, next1)); + shift(1); + state = InString; + } else if (isOctalDigit(current)) { + record16(convertOctal('0', '0', current)); + state = InString; + } else { + setDone(Bad); + } + } else if (current == 'x') + state = InHexEscape; + else if (current == 'u') + state = InUnicodeEscape; + else if (isLineTerminator()) { + nextLine(); + state = InString; + } else { + record16(singleEscape(static_cast<unsigned short>(current))); + state = InString; + } + break; + case InHexEscape: + if (isHexDigit(current) && isHexDigit(next1)) { + state = InString; + record16(convertHex(current, next1)); + shift(1); + } else if (current == stringType) { + record16('x'); + shift(1); + setDone(String); + } else { + record16('x'); + record16(current); + state = InString; + } + break; + case InUnicodeEscape: + if (isHexDigit(current) && isHexDigit(next1) && isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InString; + } else if (current == stringType) { + record16('u'); + shift(1); + setDone(String); + } else { + setDone(Bad); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + nextLine(); + terminator = true; + if (restrKeyword) { + token = ';'; + setDone(Other); + } else + state = Start; + } else if (current == -1) { + setDone(Eof); + } + break; + case InMultiLineComment: + if (current == -1) { + setDone(Bad); + } else if (isLineTerminator()) { + nextLine(); + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + } + break; + case InIdentifierOrKeyword: + case InIdentifier: + if (isIdentPart(current)) + record16(current); + else if (current == '\\') + state = InIdentifierPartUnicodeEscapeStart; + else + setDone(state == InIdentifierOrKeyword ? IdentifierOrKeyword : Identifier); + break; + case InNum0: + if (current == 'x' || current == 'X') { + record8(current); + state = InHex; + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else if (isOctalDigit(current)) { + record8(current); + state = InOctal; + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Number); + } + break; + case InHex: + if (isHexDigit(current)) { + record8(current); + } else { + setDone(Hex); + } + break; + case InOctal: + if (isOctalDigit(current)) { + record8(current); + } + else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else + setDone(Octal); + break; + case InNum: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else + setDone(Number); + break; + case InDecimal: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else + setDone(Number); + break; + case InExponentIndicator: + if (current == '+' || current == '-') { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InExponent; + } else + setDone(Bad); + break; + case InExponent: + if (isDecimalDigit(current)) { + record8(current); + } else + setDone(Number); + break; + case InIdentifierStartUnicodeEscapeStart: + if (current == 'u') + state = InIdentifierStartUnicodeEscape; + else + setDone(Bad); + break; + case InIdentifierPartUnicodeEscapeStart: + if (current == 'u') + state = InIdentifierPartUnicodeEscape; + else + setDone(Bad); + break; + case InIdentifierStartUnicodeEscape: + if (!isHexDigit(current) || !isHexDigit(next1) || !isHexDigit(next2) || !isHexDigit(next3)) { + setDone(Bad); + break; + } + token = convertUnicode(current, next1, next2, next3).uc; + shift(3); + if (!isIdentStart(token)) { + setDone(Bad); + break; + } + record16(token); + state = InIdentifier; + break; + case InIdentifierPartUnicodeEscape: + if (!isHexDigit(current) || !isHexDigit(next1) || !isHexDigit(next2) || !isHexDigit(next3)) { + setDone(Bad); + break; + } + token = convertUnicode(current, next1, next2, next3).uc; + shift(3); + if (!isIdentPart(token)) { + setDone(Bad); + break; + } + record16(token); + state = InIdentifier; + break; + default: + ASSERT(!"Unhandled state in switch statement"); + } + + // move on to the next character + if (!done) + shift(1); + if (state != Start && state != InSingleLineComment) + atLineStart = false; + } + + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad + if ((state == Number || state == Octal || state == Hex) && isIdentStart(current)) + state = Bad; + + // terminate string + m_buffer8.append('\0'); + +#ifdef KJS_DEBUG_LEX + fprintf(stderr, "line: %d ", lineNo()); + fprintf(stderr, "yytext (%x): ", m_buffer8[0]); + fprintf(stderr, "%s ", buffer8.data()); +#endif + + double dval = 0; + if (state == Number) { + dval = kjs_strtod(m_buffer8.data(), 0L); + } else if (state == Hex) { // scan hex numbers + const char* p = m_buffer8.data() + 2; + while (char c = *p++) { + dval *= 16; + dval += convertHex(c); + } + + if (dval >= mantissaOverflowLowerBound) + dval = parseIntOverflow(m_buffer8.data() + 2, p - (m_buffer8.data() + 3), 16); + + state = Number; + } else if (state == Octal) { // scan octal number + const char* p = m_buffer8.data() + 1; + while (char c = *p++) { + dval *= 8; + dval += c - '0'; + } + + if (dval >= mantissaOverflowLowerBound) + dval = parseIntOverflow(m_buffer8.data() + 1, p - (m_buffer8.data() + 2), 8); + + state = Number; + } + +#ifdef KJS_DEBUG_LEX + switch (state) { + case Eof: + printf("(EOF)\n"); + break; + case Other: + printf("(Other)\n"); + break; + case Identifier: + printf("(Identifier)/(Keyword)\n"); + break; + case String: + printf("(String)\n"); + break; + case Number: + printf("(Number)\n"); + break; + default: + printf("(unknown)"); + } +#endif + + if (state != Identifier && eatNextIdentifier) + eatNextIdentifier = false; + + restrKeyword = false; + delimited = false; + kjsyylloc.first_line = yylineno; // ??? + kjsyylloc.last_line = yylineno; + + switch (state) { + case Eof: + token = 0; + break; + case Other: + if(token == '}' || token == ';') { + delimited = true; + } + break; + case IdentifierOrKeyword: + if ((token = Lookup::find(&mainTable, m_buffer16.data(), m_buffer16.size())) < 0) { + case Identifier: + // Lookup for keyword failed, means this is an identifier + // Apply anonymous-function hack below (eat the identifier) + if (eatNextIdentifier) { + eatNextIdentifier = false; + token = lex(); + break; + } + kjsyylval.ident = makeIdentifier(m_buffer16); + token = IDENT; + break; + } + + eatNextIdentifier = false; + // Hack for "f = function somename() { ... }", too hard to get into the grammar + if (token == FUNCTION && lastToken == '=' ) + eatNextIdentifier = true; + + if (token == CONTINUE || token == BREAK || + token == RETURN || token == THROW) + restrKeyword = true; + break; + case String: + kjsyylval.string = makeUString(m_buffer16); + token = STRING; + break; + case Number: + kjsyylval.doubleValue = dval; + token = NUMBER; + break; + case Bad: +#ifdef KJS_DEBUG_LEX + fprintf(stderr, "yylex: ERROR.\n"); +#endif + error = true; + return -1; + default: + ASSERT(!"unhandled numeration value in switch"); + error = true; + return -1; + } + lastToken = token; + return token; +} + +bool Lexer::isWhiteSpace() const +{ + return current == '\t' || current == 0x0b || current == 0x0c || isSeparatorSpace(current); +} + +bool Lexer::isLineTerminator() +{ + bool cr = (current == '\r'); + bool lf = (current == '\n'); + if (cr) + skipLF = true; + else if (lf) + skipCR = true; + return cr || lf || current == 0x2028 || current == 0x2029; +} + +bool Lexer::isIdentStart(int c) +{ + return (category(c) & (Letter_Uppercase | Letter_Lowercase | Letter_Titlecase | Letter_Modifier | Letter_Other)) + || c == '$' || c == '_'; +} + +bool Lexer::isIdentPart(int c) +{ + return (category(c) & (Letter_Uppercase | Letter_Lowercase | Letter_Titlecase | Letter_Modifier | Letter_Other + | Mark_NonSpacing | Mark_SpacingCombining | Number_DecimalDigit | Punctuation_Connector)) + || c == '$' || c == '_'; +} + +static bool isDecimalDigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +bool Lexer::isHexDigit(int c) +{ + return (c >= '0' && c <= '9' || + c >= 'a' && c <= 'f' || + c >= 'A' && c <= 'F'); +} + +bool Lexer::isOctalDigit(int c) +{ + return (c >= '0' && c <= '7'); +} + +int Lexer::matchPunctuator(int c1, int c2, int c3, int c4) +{ + if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { + shift(4); + return URSHIFTEQUAL; + } else if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return STREQ; + } else if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return STRNEQ; + } else if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return URSHIFT; + } else if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return LSHIFTEQUAL; + } else if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return RSHIFTEQUAL; + } else if (c1 == '<' && c2 == '=') { + shift(2); + return LE; + } else if (c1 == '>' && c2 == '=') { + shift(2); + return GE; + } else if (c1 == '!' && c2 == '=') { + shift(2); + return NE; + } else if (c1 == '+' && c2 == '+') { + shift(2); + if (terminator) + return AUTOPLUSPLUS; + else + return PLUSPLUS; + } else if (c1 == '-' && c2 == '-') { + shift(2); + if (terminator) + return AUTOMINUSMINUS; + else + return MINUSMINUS; + } else if (c1 == '=' && c2 == '=') { + shift(2); + return EQEQ; + } else if (c1 == '+' && c2 == '=') { + shift(2); + return PLUSEQUAL; + } else if (c1 == '-' && c2 == '=') { + shift(2); + return MINUSEQUAL; + } else if (c1 == '*' && c2 == '=') { + shift(2); + return MULTEQUAL; + } else if (c1 == '/' && c2 == '=') { + shift(2); + return DIVEQUAL; + } else if (c1 == '&' && c2 == '=') { + shift(2); + return ANDEQUAL; + } else if (c1 == '^' && c2 == '=') { + shift(2); + return XOREQUAL; + } else if (c1 == '%' && c2 == '=') { + shift(2); + return MODEQUAL; + } else if (c1 == '|' && c2 == '=') { + shift(2); + return OREQUAL; + } else if (c1 == '<' && c2 == '<') { + shift(2); + return LSHIFT; + } else if (c1 == '>' && c2 == '>') { + shift(2); + return RSHIFT; + } else if (c1 == '&' && c2 == '&') { + shift(2); + return AND; + } else if (c1 == '|' && c2 == '|') { + shift(2); + return OR; + } + + switch(c1) { + case '=': + case '>': + case '<': + case ',': + case '!': + case '~': + case '?': + case ':': + case '.': + case '+': + case '-': + case '*': + case '/': + case '&': + case '|': + case '^': + case '%': + case '(': + case ')': + case '{': + case '}': + case '[': + case ']': + case ';': + shift(1); + return static_cast<int>(c1); + default: + return -1; + } +} + +unsigned short Lexer::singleEscape(unsigned short c) +{ + switch(c) { + case 'b': + return 0x08; + case 't': + return 0x09; + case 'n': + return 0x0A; + case 'v': + return 0x0B; + case 'f': + return 0x0C; + case 'r': + return 0x0D; + case '"': + return 0x22; + case '\'': + return 0x27; + case '\\': + return 0x5C; + default: + return c; + } +} + +unsigned short Lexer::convertOctal(int c1, int c2, int c3) +{ + return static_cast<unsigned short>((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0'); +} + +unsigned char Lexer::convertHex(int c) +{ + if (c >= '0' && c <= '9') + return static_cast<unsigned char>(c - '0'); + if (c >= 'a' && c <= 'f') + return static_cast<unsigned char>(c - 'a' + 10); + return static_cast<unsigned char>(c - 'A' + 10); +} + +unsigned char Lexer::convertHex(int c1, int c2) +{ + return ((convertHex(c1) << 4) + convertHex(c2)); +} + +KJS::UChar Lexer::convertUnicode(int c1, int c2, int c3, int c4) +{ + return KJS::UChar((convertHex(c1) << 4) + convertHex(c2), + (convertHex(c3) << 4) + convertHex(c4)); +} + +void Lexer::record8(int c) +{ + ASSERT(c >= 0); + ASSERT(c <= 0xff); + m_buffer8.append(static_cast<char>(c)); +} + +void Lexer::record16(int c) +{ + ASSERT(c >= 0); + ASSERT(c <= USHRT_MAX); + record16(UChar(static_cast<unsigned short>(c))); +} + +void Lexer::record16(KJS::UChar c) +{ + m_buffer16.append(c); +} + +bool Lexer::scanRegExp() +{ + m_buffer16.clear(); + bool lastWasEscape = false; + bool inBrackets = false; + + while (1) { + if (isLineTerminator() || current == -1) + return false; + else if (current != '/' || lastWasEscape == true || inBrackets == true) + { + // keep track of '[' and ']' + if (!lastWasEscape) { + if ( current == '[' && !inBrackets ) + inBrackets = true; + if ( current == ']' && inBrackets ) + inBrackets = false; + } + record16(current); + lastWasEscape = + !lastWasEscape && (current == '\\'); + } else { // end of regexp + m_pattern = UString(m_buffer16); + m_buffer16.clear(); + shift(1); + break; + } + shift(1); + } + + while (isIdentPart(current)) { + record16(current); + shift(1); + } + m_flags = UString(m_buffer16); + + return true; +} + +void Lexer::clear() +{ + deleteAllValues(m_strings); + Vector<UString*> newStrings; + newStrings.reserveCapacity(initialStringTableCapacity); + m_strings.swap(newStrings); + + deleteAllValues(m_identifiers); + Vector<KJS::Identifier*> newIdentifiers; + newIdentifiers.reserveCapacity(initialStringTableCapacity); + m_identifiers.swap(newIdentifiers); + + Vector<char> newBuffer8; + newBuffer8.reserveCapacity(initialReadBufferCapacity); + m_buffer8.swap(newBuffer8); + + Vector<UChar> newBuffer16; + newBuffer16.reserveCapacity(initialReadBufferCapacity); + m_buffer16.swap(newBuffer16); + + m_pattern = 0; + m_flags = 0; +} + +Identifier* Lexer::makeIdentifier(const Vector<KJS::UChar>& buffer) +{ + KJS::Identifier* identifier = new KJS::Identifier(buffer.data(), buffer.size()); + m_identifiers.append(identifier); + return identifier; +} + +UString* Lexer::makeUString(const Vector<KJS::UChar>& buffer) +{ + UString* string = new UString(buffer); + m_strings.append(string); + return string; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/lexer.h b/JavaScriptCore/kjs/lexer.h new file mode 100644 index 0000000..1ce27af --- /dev/null +++ b/JavaScriptCore/kjs/lexer.h @@ -0,0 +1,149 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 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. + * + */ + +#ifndef Lexer_h +#define Lexer_h + +#include "ustring.h" +#include <wtf/Vector.h> + +namespace KJS { + + class Identifier; + class RegExp; + + class Lexer : Noncopyable { + public: + void setCode(int startingLineNumber, const UChar *c, unsigned int len); + int lex(); + + int lineNo() const { return yylineno; } + + bool prevTerminator() const { return terminator; } + + enum State { Start, + IdentifierOrKeyword, + Identifier, + InIdentifierOrKeyword, + InIdentifier, + InIdentifierStartUnicodeEscapeStart, + InIdentifierStartUnicodeEscape, + InIdentifierPartUnicodeEscapeStart, + InIdentifierPartUnicodeEscape, + InSingleLineComment, + InMultiLineComment, + InNum, + InNum0, + InHex, + InOctal, + InDecimal, + InExponentIndicator, + InExponent, + Hex, + Octal, + Number, + String, + Eof, + InString, + InEscapeSequence, + InHexEscape, + InUnicodeEscape, + Other, + Bad }; + + bool scanRegExp(); + const UString& pattern() const { return m_pattern; } + const UString& flags() const { return m_flags; } + + static unsigned char convertHex(int); + static unsigned char convertHex(int c1, int c2); + static UChar convertUnicode(int c1, int c2, int c3, int c4); + static bool isIdentStart(int); + static bool isIdentPart(int); + static bool isHexDigit(int); + + bool sawError() const { return error; } + + void clear(); + + private: + friend Lexer& lexer(); + Lexer(); + + int yylineno; + bool done; + Vector<char> m_buffer8; + Vector<UChar> m_buffer16; + bool terminator; + bool restrKeyword; + // encountered delimiter like "'" and "}" on last run + bool delimited; + bool skipLF; + bool skipCR; + bool eatNextIdentifier; + int stackToken; + int lastToken; + + State state; + void setDone(State); + unsigned int pos; + void shift(unsigned int p); + void nextLine(); + int lookupKeyword(const char *); + + bool isWhiteSpace() const; + bool isLineTerminator(); + static bool isOctalDigit(int); + + int matchPunctuator(int c1, int c2, int c3, int c4); + static unsigned short singleEscape(unsigned short); + static unsigned short convertOctal(int c1, int c2, int c3); + + void record8(int); + void record16(int); + void record16(UChar); + + KJS::Identifier* makeIdentifier(const Vector<UChar>& buffer); + UString* makeUString(const Vector<UChar>& buffer); + + const UChar* code; + unsigned int length; + int yycolumn; + int atLineStart; + bool error; + + // current and following unicode characters (int to allow for -1 for end-of-file marker) + int current, next1, next2, next3; + + Vector<UString*> m_strings; + Vector<KJS::Identifier*> m_identifiers; + + UString m_pattern; + UString m_flags; + }; + + Lexer& lexer(); // Returns the singletone JavaScript lexer. + +} // namespace KJS + +#endif // Lexer_h diff --git a/JavaScriptCore/kjs/list.cpp b/JavaScriptCore/kjs/list.cpp new file mode 100644 index 0000000..5cc7cc2 --- /dev/null +++ b/JavaScriptCore/kjs/list.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 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. + * + */ + +#include "config.h" +#include "list.h" + +using std::min; + +namespace KJS { + +void List::getSlice(int startIndex, List& result) const +{ + const_iterator start = min(begin() + startIndex, end()); + result.m_vector.appendRange(start, end()); +} + +List::ListSet& List::markSet() +{ + static ListSet staticMarkSet; + return staticMarkSet; +} + +void List::markProtectedListsSlowCase() +{ + ListSet::iterator end = markSet().end(); + for (ListSet::iterator it = markSet().begin(); it != end; ++it) { + List* list = *it; + + iterator end2 = list->end(); + for (iterator it2 = list->begin(); it2 != end2; ++it2) { + JSValue* v = *it2; + if (!v->marked()) + v->mark(); + } + } +} + +void List::expandAndAppend(JSValue* v) +{ + ASSERT(m_vector.size() == m_vector.capacity()); + + // 4x growth would be excessive for a normal vector, but it's OK for Lists + // because they're short-lived. + m_vector.reserveCapacity(m_vector.capacity() * 4); + + // As long as our size stays within our Vector's inline + // capacity, all our values are allocated on the stack, and + // therefore don't need explicit marking. Once our size exceeds + // our Vector's inline capacity, though, our values move to the + // heap, where they do need explicit marking. + if (!m_isInMarkSet) { + markSet().add(this); + m_isInMarkSet = true; + } + + m_vector.uncheckedAppend(v); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/list.h b/JavaScriptCore/kjs/list.h new file mode 100644 index 0000000..6a43e08 --- /dev/null +++ b/JavaScriptCore/kjs/list.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007 Apple Computer, 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. + * + */ + +#ifndef KJS_LIST_H +#define KJS_LIST_H + +#include <kjs/value.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace KJS { + + class JSValue; + class List; + + class List : Noncopyable { + private: + typedef Vector<JSValue*, 8> VectorType; + typedef HashSet<List*> ListSet; + + public: + typedef VectorType::iterator iterator; + typedef VectorType::const_iterator const_iterator; + + List() + : m_isInMarkSet(false) + { + } + + ~List() + { + if (m_isInMarkSet) + markSet().remove(this); + } + + size_t size() const { return m_vector.size(); } + bool isEmpty() const { return m_vector.isEmpty(); } + + JSValue* at(size_t i) const + { + if (i < m_vector.size()) + return m_vector.at(i); + return jsUndefined(); + } + + JSValue* operator[](int i) const { return at(i); } + + void clear() { m_vector.clear(); } + + void append(JSValue* v) + { + if (m_vector.size() < m_vector.capacity()) + m_vector.uncheckedAppend(v); + else + // Putting the slow "expand and append" case all in one + // function measurably improves the performance of the fast + // "just append" case. + expandAndAppend(v); + } + + void getSlice(int startIndex, List& result) const; + + iterator begin() { return m_vector.begin(); } + iterator end() { return m_vector.end(); } + + const_iterator begin() const { return m_vector.begin(); } + const_iterator end() const { return m_vector.end(); } + + static void markProtectedLists() + { + if (!markSet().size()) + return; + markProtectedListsSlowCase(); + } + + private: + static ListSet& markSet(); + static void markProtectedListsSlowCase(); + + void expandAndAppend(JSValue*); + + VectorType m_vector; + bool m_isInMarkSet; + + private: + // Prohibits new / delete, which would break GC. + void* operator new(size_t); + void operator delete(void*); + + void* operator new[](size_t); + void operator delete[](void*); + + void* operator new(size_t, void*); + void operator delete(void*, size_t); + }; + +} // namespace KJS + +#endif // KJS_LIST_H diff --git a/JavaScriptCore/kjs/lookup.cpp b/JavaScriptCore/kjs/lookup.cpp new file mode 100644 index 0000000..95dc5cb --- /dev/null +++ b/JavaScriptCore/kjs/lookup.cpp @@ -0,0 +1,81 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * 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 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 <wtf/Assertions.h> + +namespace KJS { + +static inline bool keysMatch(const UChar* c, unsigned len, const char* s) +{ + // FIXME: This can run off the end of |s| if |c| has a U+0000 character in it. + const char* end = s + len; + for (; s != end; c++, s++) + if (c->uc != *s) + return false; + return *s == 0; +} + +static inline const HashEntry* findEntry(const struct HashTable* table, unsigned int hash, + const UChar* c, unsigned int len) +{ + ASSERT(table->type == 3); + + const HashEntry* e = &table->entries[hash & table->hashSizeMask]; + + if (!e->s) + return 0; + + do { + // compare strings + if (keysMatch(c, len, e->s)) + return e; + + // try next bucket + e = e->next; + } while (e); + return 0; +} + +const HashEntry* Lookup::findEntry(const struct HashTable* table, const Identifier& s) +{ + return KJS::findEntry(table, s.ustring().rep()->computedHash(), s.data(), s.size()); +} + +int Lookup::find(const struct HashTable *table, const UChar *c, unsigned int len) +{ + const HashEntry *entry = KJS::findEntry(table, UString::Rep::computeHash(c, len), c, len); + if (entry) + return entry->value.intValue; + return -1; +} + +int Lookup::find(const struct HashTable* table, const Identifier& s) +{ + const HashEntry* entry = KJS::findEntry(table, s.ustring().rep()->computedHash(), s.data(), s.size()); + if (entry) + return entry->value.intValue; + return -1; +} + +} diff --git a/JavaScriptCore/kjs/lookup.h b/JavaScriptCore/kjs/lookup.h new file mode 100644 index 0000000..d477f88 --- /dev/null +++ b/JavaScriptCore/kjs/lookup.h @@ -0,0 +1,340 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef KJS_lookup_h +#define KJS_lookup_h + +#include "ExecState.h" +#include "function.h" +#include "identifier.h" +#include "JSGlobalObject.h" +#include "object.h" +#include <stdio.h> +#include <wtf/Assertions.h> + +namespace KJS { + + /** + * An entry in a hash table. + */ + struct HashEntry { + /** + * s is the key (e.g. a property name) + */ + const char* s; + + /** + * value is the result value (enum value for properties and a function pointer to a constructor factory for functions) + */ + union { + intptr_t intValue; + PrototypeFunction::JSMemberFunction functionValue; + } value; + + /** + * attr is a set for flags (e.g. the property flags, see object.h) + */ + unsigned char attr; + /** + * params is another number. For property hashtables, it is used to + * denote the number of argument of the function + */ + short int params; + /** + * next is the pointer to the next entry for the same hash value + */ + const HashEntry* next; + }; + + /** + * A hash table + * Usually the hashtable is generated by the create_hash_table script, from a .table file. + * + * The implementation uses an array of entries, "size" is the total size of that array. + * The entries between 0 and hashSize-1 are the entry points + * for each hash value, and the entries between hashSize and size-1 + * are the overflow entries for the hash values that need one. + * The "next" pointer of the entry links entry points to overflow entries, + * and links overflow entries between them. + */ + struct HashTable { + /** + * type is a version number. Currently always 2 + */ + int type; + /** + * size is the total number of entries in the hashtable, including the null entries, + * i.e. the size of the "entries" array. + * Used to iterate over all entries in the table + */ + int size; + /** + * pointer to the array of entries + * Mind that some entries in the array are null (0,0,0,0). + */ + const HashEntry* entries; + /** + * the maximum value for the hash minus 1. Always smaller than size. + */ + int hashSizeMask; + }; + + /** + * @short Fast keyword lookup. + */ + class Lookup { + public: + /** + * Find an entry in the table, and return its value (i.e. the value field of HashEntry) + */ + static int find(const struct HashTable*, const Identifier&); + static int find(const struct HashTable*, const UChar*, unsigned int len); + + /** + * Find an entry in the table, and return the entry + * This variant gives access to the other attributes of the entry, + * especially the attr field. + */ + static const HashEntry* findEntry(const struct HashTable*, const Identifier&); + + }; + + class ExecState; + class UString; + /** + * @internal + * Helper for getStaticFunctionSlot and getStaticPropertySlot + */ + inline JSValue* staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) + { + // Look for cached value in dynamic map of properties (in JSObject) + JSObject* thisObj = slot.slotBase(); + JSValue* cachedVal = thisObj->getDirect(propertyName); + if (cachedVal) + return cachedVal; + + const HashEntry* entry = slot.staticEntry(); + JSValue* val = new PrototypeFunction(exec, entry->params, propertyName, entry->value.functionValue); + thisObj->putDirect(propertyName, val, entry->attr); + return val; + } + + /** + * @internal + * Helper for getStaticValueSlot and getStaticPropertySlot + */ + template <class ThisImp> + inline JSValue* staticValueGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot) + { + ThisImp* thisObj = static_cast<ThisImp*>(slot.slotBase()); + const HashEntry* entry = slot.staticEntry(); + return thisObj->getValueProperty(exec, entry->value.intValue); + } + + /** + * Helper method for property lookups + * + * 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 arguments: + * @param FuncImp the class which implements this object's functions + * @param ThisImp the class of "this". It must implement the getValueProperty(exec,token) method, + * for non-function properties. + * @param ParentImp the class of the parent, to propagate the lookup. + * + * Method arguments: + * @param exec execution state, as usual + * @param propertyName the property we're looking for + * @param table the static hashtable for this class + * @param thisObj "this" + */ + template <class ThisImp, class ParentImp> + inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, + ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + if (entry->attr & Function) + slot.setStaticEntry(thisObj, entry, staticFunctionGetter); + else + slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>); + + 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) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) // not found, forward to parent + return static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + ASSERT(entry->attr & Function); + + slot.setStaticEntry(thisObj, entry, staticFunctionGetter); + return true; + } + + /** + * Simplified version of getStaticPropertySlot in case there are no functions, only "values". + * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. + */ + template <class ThisImp, class ParentImp> + inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, + ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + ASSERT(!(entry->attr & Function)); + + slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>); + return true; + } + + /** + * This one is for "put". + * It looks up a hash entry for the property to be set. If an entry + * is found it sets the value and returns true, else it returns false. + */ + template <class ThisImp> + inline bool lookupPut(ExecState* exec, const Identifier& propertyName, + JSValue* value, const HashTable* table, ThisImp* thisObj) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) + return false; + + if (entry->attr & Function) // function: put as override property + thisObj->putDirect(propertyName, value); + else if (!(entry->attr & ReadOnly)) + thisObj->putValueProperty(exec, entry->value.intValue, value); + + return true; + } + + /** + * This one is for "put". + * It calls lookupPut<ThisImp>() to set the value. If that call + * returns false (meaning no entry in the hash table was found), + * then it calls put() on the ParentImp class. + */ + template <class ThisImp, class ParentImp> + inline void lookupPut(ExecState* exec, const Identifier& propertyName, + JSValue* value, const HashTable* table, ThisImp* thisObj) + { + if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj)) + thisObj->ParentImp::put(exec, propertyName, value); // not found: forward to parent + } + + /** + * This template method retrieves or create an object that is unique + * (for a given global object) The first time this is called (for a given + * property name), the Object will be constructed, and set as a property + * of the global object. Later calls will simply retrieve that cached object. + * Note that the object constructor must take 1 argument, exec. + */ + template <class ClassCtor> + inline JSObject* cacheGlobalObject(ExecState* exec, const Identifier& propertyName) + { + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + JSValue* obj = globalObject->getDirect(propertyName); + if (obj) { + ASSERT(obj->isObject()); + return static_cast<JSObject* >(obj); + } + JSObject* newObject = new ClassCtor(exec); + globalObject->putDirect(propertyName, newObject, DontEnum); + return newObject; + } + +} // namespace + +/** + * Helpers to define prototype objects (each of which simply implements + * the functions for a type of objects). + * Sorry for this not being very readable, but it actually saves much copy-n-paste. + * ParentPrototype is not our base class, it's the object we use as fallback. + * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.), + * not one in each derived class. So we link the (unique) prototypes between them. + * + * Using those macros is very simple: define the hashtable (e.g. "DOMNodePrototypeTable"), then + * KJS_DEFINE_PROTOTYPE(DOMNodePrototype) + * KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodePrototype, DOMNodePrototypeFunction) + * and use DOMNodePrototype::self(exec) as prototype in the DOMNode constructor. + * If the prototype has a "parent prototype", e.g. DOMElementPrototype falls back on DOMNodePrototype, + * then the first line will use KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE, with DOMNodePrototype as the second argument. + */ + +// These macros assume that a prototype's only properties are functions +#define KJS_DEFINE_PROTOTYPE(ClassPrototype) \ + class ClassPrototype : public KJS::JSObject { \ + public: \ + static KJS::JSObject* self(KJS::ExecState* exec); \ + virtual const KJS::ClassInfo* classInfo() const { return &info; } \ + static const KJS::ClassInfo info; \ + bool getOwnPropertySlot(KJS::ExecState* , const KJS::Identifier&, KJS::PropertySlot&); \ + ClassPrototype(KJS::ExecState* exec) \ + : KJS::JSObject(exec->lexicalGlobalObject()->objectPrototype()) { } \ + \ + }; + +#define KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(ClassPrototype, ClassPrototypePrototype) \ + class ClassPrototype : public KJS::JSObject { \ + public: \ + static KJS::JSObject* self(KJS::ExecState* exec); \ + virtual const KJS::ClassInfo* classInfo() const { return &info; } \ + static const KJS::ClassInfo info; \ + bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&); \ + ClassPrototype(KJS::ExecState* exec) \ + : KJS::JSObject(ClassPrototypePrototype::self(exec)) { } \ + \ + }; + +#define KJS_IMPLEMENT_PROTOTYPE(ClassName, ClassPrototype) \ + const ClassInfo ClassPrototype::info = { ClassName"Prototype", 0, &ClassPrototype##Table }; \ + JSObject* ClassPrototype::self(ExecState* exec) \ + { \ + static Identifier* prototypeIdentifier = new Identifier("[[" ClassName ".prototype]]"); \ + return KJS::cacheGlobalObject<ClassPrototype>(exec, *prototypeIdentifier); \ + } \ + bool ClassPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) \ + { \ + return getStaticFunctionSlot<JSObject>(exec, &ClassPrototype##Table, this, propertyName, slot); \ + } + +#endif // KJS_lookup_h diff --git a/JavaScriptCore/kjs/math_object.cpp b/JavaScriptCore/kjs/math_object.cpp new file mode 100644 index 0000000..805efda --- /dev/null +++ b/JavaScriptCore/kjs/math_object.cpp @@ -0,0 +1,243 @@ +/* + * 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 "math_object.h" +#include "math_object.lut.h" + +#include "operations.h" +#include <time.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> + +namespace KJS { + +// ------------------------------ MathObjectImp -------------------------------- + +const ClassInfo MathObjectImp::info = { "Math", 0, &mathTable }; + +/* Source for math_object.lut.h +@begin mathTable 21 + E MathObjectImp::Euler DontEnum|DontDelete|ReadOnly + LN2 MathObjectImp::Ln2 DontEnum|DontDelete|ReadOnly + LN10 MathObjectImp::Ln10 DontEnum|DontDelete|ReadOnly + LOG2E MathObjectImp::Log2E DontEnum|DontDelete|ReadOnly + LOG10E MathObjectImp::Log10E DontEnum|DontDelete|ReadOnly + PI MathObjectImp::Pi DontEnum|DontDelete|ReadOnly + SQRT1_2 MathObjectImp::Sqrt1_2 DontEnum|DontDelete|ReadOnly + SQRT2 MathObjectImp::Sqrt2 DontEnum|DontDelete|ReadOnly + 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 +*/ + +MathObjectImp::MathObjectImp(ExecState*, ObjectPrototype* objectPrototype) + : JSObject(objectPrototype) +{ +} + +// ECMA 15.8 + +bool MathObjectImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticPropertySlot<MathObjectImp, JSObject>(exec, &mathTable, this, propertyName, slot); +} + +JSValue* MathObjectImp::getValueProperty(ExecState*, int token) const +{ + switch (token) { + case Euler: + return jsNumber(exp(1.0)); + case Ln2: + return jsNumber(log(2.0)); + case Ln10: + return jsNumber(log(10.0)); + case Log2E: + return jsNumber(1.0 / log(2.0)); + case Log10E: + return jsNumber(1.0 / log(10.0)); + case Pi: + return jsNumber(piDouble); + case Sqrt1_2: + return jsNumber(sqrt(0.5)); + case Sqrt2: + return jsNumber(sqrt(2.0)); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +// ------------------------------ Functions -------------------------------- + +JSValue* mathProtoFuncAbs(ExecState* exec, JSObject*, const List& args) +{ + double arg = args[0]->toNumber(exec); + return signbit(arg) ? jsNumber(-arg) : jsNumber(arg); +} + +JSValue* mathProtoFuncACos(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(acos(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncASin(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(asin(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncATan(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(atan(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncATan2(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(atan2(args[0]->toNumber(exec), args[1]->toNumber(exec))); +} + +JSValue* mathProtoFuncCeil(ExecState* exec, JSObject*, const List& args) +{ + double arg = args[0]->toNumber(exec); + if (signbit(arg) && arg > -1.0) + return jsNumber(-0.0); + return jsNumber(ceil(arg)); +} + +JSValue* mathProtoFuncCos(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(cos(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncExp(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(exp(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncFloor(ExecState* exec, JSObject*, const List& args) +{ + double arg = args[0]->toNumber(exec); + if (signbit(arg) && arg == 0.0) + return jsNumber(-0.0); + return jsNumber(floor(arg)); +} + +JSValue* mathProtoFuncLog(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(log(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncMax(ExecState* exec, JSObject*, const List& args) +{ + unsigned argsCount = args.size(); + double result = -Inf; + for (unsigned k = 0; k < argsCount; ++k) { + double val = args[k]->toNumber(exec); + if (isnan(val)) { + result = NaN; + break; + } + if (val > result || (val == 0 && result == 0 && !signbit(val))) + result = val; + } + return jsNumber(result); +} + +JSValue* mathProtoFuncMin(ExecState* exec, JSObject*, const List& args) +{ + unsigned argsCount = args.size(); + double result = +Inf; + for (unsigned k = 0; k < argsCount; ++k) { + double val = args[k]->toNumber(exec); + if (isnan(val)) { + result = NaN; + break; + } + if (val < result || (val == 0 && result == 0 && signbit(val))) + result = val; + } + return jsNumber(result); +} + +JSValue* mathProtoFuncPow(ExecState* exec, JSObject*, const List& args) +{ + // ECMA 15.8.2.1.13 + + double arg = args[0]->toNumber(exec); + double arg2 = args[1]->toNumber(exec); + + if (isnan(arg2)) + return jsNumber(NaN); + if (isinf(arg2) && fabs(arg) == 1) + return jsNumber(NaN); + return jsNumber(pow(arg, arg2)); +} + +static bool didInitRandom; + +JSValue* mathProtoFuncRandom(ExecState*, JSObject*, const List&) +{ + if (!didInitRandom) { + wtf_random_init(); + didInitRandom = true; + } + return jsNumber(wtf_random()); +} + +JSValue* mathProtoFuncRound(ExecState* exec, JSObject*, const List& args) +{ + double arg = args[0]->toNumber(exec); + if (signbit(arg) && arg >= -0.5) + return jsNumber(-0.0); + return jsNumber(floor(arg + 0.5)); +} + +JSValue* mathProtoFuncSin(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(sin(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncSqrt(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(sqrt(args[0]->toNumber(exec))); +} + +JSValue* mathProtoFuncTan(ExecState* exec, JSObject*, const List& args) +{ + return jsNumber(tan(args[0]->toNumber(exec))); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/math_object.h b/JavaScriptCore/kjs/math_object.h new file mode 100644 index 0000000..72e442f --- /dev/null +++ b/JavaScriptCore/kjs/math_object.h @@ -0,0 +1,65 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef MATH_OBJECT_H_ +#define MATH_OBJECT_H_ + +#include "function_object.h" +#include "lookup.h" + +namespace KJS { + + class MathObjectImp : public JSObject { + public: + MathObjectImp(ExecState*, ObjectPrototype*); + + bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + JSValue* getValueProperty(ExecState*, int token) const; + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + enum { Euler, Ln2, Ln10, Log2E, Log10E, Pi, Sqrt1_2, Sqrt2 }; + }; + + // Functions + JSValue* mathProtoFuncAbs(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncACos(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncASin(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncATan(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncATan2(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncCeil(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncCos(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncExp(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncFloor(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncLog(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncMax(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncMin(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncPow(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncRandom(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncRound(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncSin(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncSqrt(ExecState*, JSObject*, const List&); + JSValue* mathProtoFuncTan(ExecState*, JSObject*, const List&); + +} // namespace KJS + +#endif // MATH_OBJECT_H_ diff --git a/JavaScriptCore/kjs/nodes.cpp b/JavaScriptCore/kjs/nodes.cpp new file mode 100644 index 0000000..b149d59 --- /dev/null +++ b/JavaScriptCore/kjs/nodes.cpp @@ -0,0 +1,4710 @@ +/* +* Copyright (C) 1999-2002 Harri Porten (porten@kde.org) +* Copyright (C) 2001 Peter Kelly (pmk@post.com) +* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. +* Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) +* Copyright (C) 2007 Maks Orlovich +* 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 "nodes.h" + +#include "ExecState.h" +#include "JSGlobalObject.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "array_object.h" +#include "debugger.h" +#include "function_object.h" +#include "lexer.h" +#include "operations.h" +#include "regexp_object.h" +#include <math.h> +#include <wtf/Assertions.h> +#include <wtf/HashCountedSet.h> +#include <wtf/HashSet.h> +#include <wtf/MathExtras.h> + +namespace KJS { + +class FunctionBodyNodeWithDebuggerHooks : public FunctionBodyNode { +public: + FunctionBodyNodeWithDebuggerHooks(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; +}; + +#if COMPILER(GCC) +#define UNLIKELY(x) \ + __builtin_expect ((x), 0) +#else +#define UNLIKELY(x) x +#endif + +#define KJS_CHECKEXCEPTION \ +if (UNLIKELY(exec->hadException())) \ + return rethrowException(exec); + +#define KJS_CHECKEXCEPTIONVALUE \ +if (UNLIKELY(exec->hadException())) { \ + handleException(exec); \ + return jsUndefined(); \ +} + +#define KJS_CHECKEXCEPTIONNUMBER \ +if (UNLIKELY(exec->hadException())) { \ + handleException(exec); \ + return 0; \ +} + +#define KJS_CHECKEXCEPTIONBOOLEAN \ +if (UNLIKELY(exec->hadException())) { \ + handleException(exec); \ + return false; \ +} + +#define KJS_CHECKEXCEPTIONVOID \ +if (UNLIKELY(exec->hadException())) { \ + handleException(exec); \ + return; \ +} + +#if !ASSERT_DISABLED +static inline bool canSkipLookup(ExecState* exec, const Identifier& ident) +{ + // Static lookup in EvalCode is impossible because variables aren't DontDelete. + // Static lookup in GlobalCode may be possible, but we haven't implemented support for it yet. + if (exec->codeType() != FunctionCode) + return false; + + // Static lookup is impossible when something dynamic has been added to the front of the scope chain. + if (exec->variableObject() != exec->scopeChain().top()) + return false; + + // Static lookup is impossible if the symbol isn't statically declared. + if (!exec->variableObject()->symbolTable().contains(ident.ustring().rep())) + return false; + + return true; +} +#endif + +static inline bool isConstant(const LocalStorage& localStorage, size_t index) +{ + ASSERT(index < localStorage.size()); + return localStorage[index].attributes & ReadOnly; +} + +// ------------------------------ Node ----------------------------------------- + +#ifndef NDEBUG +#ifndef LOG_CHANNEL_PREFIX +#define LOG_CHANNEL_PREFIX Log +#endif +static WTFLogChannel LogKJSNodeLeaks = { 0x00000000, "", WTFLogChannelOn }; + +struct ParserRefCountedCounter { + static unsigned count; + ParserRefCountedCounter() + { + if (count) + LOG(KJSNodeLeaks, "LEAK: %u KJS::Node\n", count); + } +}; +unsigned ParserRefCountedCounter::count = 0; +static ParserRefCountedCounter parserRefCountedCounter; +#endif + +static HashSet<ParserRefCounted*>* newTrackedObjects; +static HashCountedSet<ParserRefCounted*>* trackedObjectExtraRefCounts; + +ParserRefCounted::ParserRefCounted() +{ +#ifndef NDEBUG + ++ParserRefCountedCounter::count; +#endif + if (!newTrackedObjects) + newTrackedObjects = new HashSet<ParserRefCounted*>; + newTrackedObjects->add(this); + ASSERT(newTrackedObjects->contains(this)); +} + +ParserRefCounted::~ParserRefCounted() +{ +#ifndef NDEBUG + --ParserRefCountedCounter::count; +#endif +} + +void ParserRefCounted::ref() +{ + // bumping from 0 to 1 is just removing from the new nodes set + if (newTrackedObjects) { + HashSet<ParserRefCounted*>::iterator it = newTrackedObjects->find(this); + if (it != newTrackedObjects->end()) { + newTrackedObjects->remove(it); + ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this)); + return; + } + } + + ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); + + if (!trackedObjectExtraRefCounts) + trackedObjectExtraRefCounts = new HashCountedSet<ParserRefCounted*>; + trackedObjectExtraRefCounts->add(this); +} + +void ParserRefCounted::deref() +{ + ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); + + if (!trackedObjectExtraRefCounts) { + delete this; + return; + } + + HashCountedSet<ParserRefCounted*>::iterator it = trackedObjectExtraRefCounts->find(this); + if (it == trackedObjectExtraRefCounts->end()) + delete this; + else + trackedObjectExtraRefCounts->remove(it); +} + +unsigned ParserRefCounted::refcount() +{ + if (newTrackedObjects && newTrackedObjects->contains(this)) { + ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this)); + return 0; + } + + ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); + + if (!trackedObjectExtraRefCounts) + return 1; + + return 1 + trackedObjectExtraRefCounts->count(this); +} + +void ParserRefCounted::deleteNewObjects() +{ + if (!newTrackedObjects) + return; + +#ifndef NDEBUG + HashSet<ParserRefCounted*>::iterator end = newTrackedObjects->end(); + for (HashSet<ParserRefCounted*>::iterator it = newTrackedObjects->begin(); it != end; ++it) + ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(*it)); +#endif + deleteAllValues(*newTrackedObjects); + delete newTrackedObjects; + newTrackedObjects = 0; +} + +Node::Node() + : m_expectedReturnType(ObjectType) +{ + m_line = lexer().lineNo(); +} + +Node::Node(JSType expectedReturn) + : m_expectedReturnType(expectedReturn) +{ + m_line = lexer().lineNo(); +} + +double ExpressionNode::evaluateToNumber(ExecState* exec) +{ + JSValue* value = evaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return value->toNumber(exec); +} + +bool ExpressionNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* value = evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return value->toBoolean(exec); +} + +int32_t ExpressionNode::evaluateToInt32(ExecState* exec) +{ + JSValue* value = evaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return value->toInt32(exec); +} + +uint32_t ExpressionNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* value = evaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return value->toUInt32(exec); +} + +static void substitute(UString& string, const UString& substring) KJS_FAST_CALL; +static void substitute(UString& string, const UString& substring) +{ + int position = string.find("%s"); + ASSERT(position != -1); + UString newString = string.substr(0, position); + newString.append(substring); + newString.append(string.substr(position + 2)); + string = newString; +} + +static inline int currentSourceId(ExecState* exec) KJS_FAST_CALL; +static inline int currentSourceId(ExecState* exec) +{ + return exec->scopeNode()->sourceId(); +} + +static inline const UString& currentSourceURL(ExecState* exec) KJS_FAST_CALL; +static inline const UString& currentSourceURL(ExecState* exec) +{ + return exec->scopeNode()->sourceURL(); +} + +JSValue* Node::setErrorCompletion(ExecState* exec, ErrorType e, const char* msg) +{ + return exec->setThrowCompletion(Error::create(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec))); +} + +JSValue* Node::setErrorCompletion(ExecState* exec, ErrorType e, const char* msg, const Identifier& ident) +{ + UString message = msg; + substitute(message, ident.ustring()); + return exec->setThrowCompletion(Error::create(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec))); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg) +{ + return KJS::throwError(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, const char* string) +{ + UString message = msg; + substitute(message, string); + return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* expr) +{ + UString message = msg; + substitute(message, v->toString(exec)); + substitute(message, expr->toString()); + return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, const Identifier& label) +{ + UString message = msg; + substitute(message, label.ustring()); + return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* e1, Node* e2) +{ + UString message = msg; + substitute(message, v->toString(exec)); + substitute(message, e1->toString()); + substitute(message, e2->toString()); + return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* expr, const Identifier& label) +{ + UString message = msg; + substitute(message, v->toString(exec)); + substitute(message, expr->toString()); + substitute(message, label.ustring()); + return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, const Identifier& label) +{ + UString message = msg; + substitute(message, v->toString(exec)); + substitute(message, label.ustring()); + return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)); +} + +JSValue* Node::throwUndefinedVariableError(ExecState* exec, const Identifier& ident) +{ + return throwError(exec, ReferenceError, "Can't find variable: %s", ident); +} + +void Node::handleException(ExecState* exec) +{ + handleException(exec, exec->exception()); +} + +void Node::handleException(ExecState* exec, JSValue* exceptionValue) +{ + if (exceptionValue->isObject()) { + JSObject* exception = static_cast<JSObject*>(exceptionValue); + if (!exception->hasProperty(exec, "line") && !exception->hasProperty(exec, "sourceURL")) { + exception->put(exec, "line", jsNumber(m_line)); + exception->put(exec, "sourceURL", jsString(currentSourceURL(exec))); + } + } + Debugger* dbg = exec->dynamicGlobalObject()->debugger(); + if (dbg && !dbg->hasHandledException(exec, exceptionValue)) + dbg->exception(exec, currentSourceId(exec), m_line, exceptionValue); +} + +NEVER_INLINE JSValue* Node::rethrowException(ExecState* exec) +{ + JSValue* exception = exec->exception(); + exec->clearException(); + handleException(exec, exception); + return exec->setThrowCompletion(exception); +} + +// ------------------------------ StatementNode -------------------------------- + +StatementNode::StatementNode() + : m_lastLine(-1) +{ + m_line = -1; +} + +void StatementNode::setLoc(int firstLine, int lastLine) +{ + m_line = firstLine; + m_lastLine = lastLine; +} + +// ------------------------------ SourceElements -------------------------------- + +void SourceElements::append(PassRefPtr<StatementNode> statement) +{ + if (statement->isEmptyStatement()) + return; + + if (Debugger::debuggersPresent) + m_statements.append(new BreakpointCheckStatement(statement)); + else + m_statements.append(statement); +} + +// ------------------------------ BreakpointCheckStatement -------------------------------- + +BreakpointCheckStatement::BreakpointCheckStatement(PassRefPtr<StatementNode> statement) + : m_statement(statement) +{ + ASSERT(m_statement); +} + +JSValue* BreakpointCheckStatement::execute(ExecState* exec) +{ + if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) + if (!debugger->atStatement(exec, currentSourceId(exec), m_statement->firstLine(), m_statement->lastLine())) + return exec->setNormalCompletion(); + return m_statement->execute(exec); +} + +void BreakpointCheckStatement::streamTo(SourceStream& stream) const +{ + m_statement->streamTo(stream); +} + +void BreakpointCheckStatement::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_statement.get()); +} + +// ------------------------------ NullNode ------------------------------------- + +JSValue* NullNode::evaluate(ExecState* ) +{ + return jsNull(); +} + +// ------------------------------ FalseNode ---------------------------------- + +JSValue* FalseNode::evaluate(ExecState*) +{ + return jsBoolean(false); +} + +// ------------------------------ TrueNode ---------------------------------- + +JSValue* TrueNode::evaluate(ExecState*) +{ + return jsBoolean(true); +} + +// ------------------------------ NumberNode ----------------------------------- + +JSValue* NumberNode::evaluate(ExecState*) +{ + // Number nodes are only created when the number can't fit in a JSImmediate, so no need to check again. + return jsNumberCell(m_double); +} + +double NumberNode::evaluateToNumber(ExecState*) +{ + return m_double; +} + +bool NumberNode::evaluateToBoolean(ExecState*) +{ + return m_double < 0.0 || m_double > 0.0; // false for NaN as well as 0 +} + +int32_t NumberNode::evaluateToInt32(ExecState*) +{ + return JSValue::toInt32(m_double); +} + +uint32_t NumberNode::evaluateToUInt32(ExecState*) +{ + return JSValue::toUInt32(m_double); +} + +// ------------------------------ ImmediateNumberNode ----------------------------------- + +JSValue* ImmediateNumberNode::evaluate(ExecState*) +{ + return m_value; +} + +int32_t ImmediateNumberNode::evaluateToInt32(ExecState*) +{ + return JSImmediate::getTruncatedInt32(m_value); +} + +uint32_t ImmediateNumberNode::evaluateToUInt32(ExecState*) +{ + uint32_t i; + if (JSImmediate::getTruncatedUInt32(m_value, i)) + return i; + bool ok; + return JSValue::toUInt32SlowCase(m_double, ok); +} + +// ------------------------------ StringNode ----------------------------------- + +JSValue* StringNode::evaluate(ExecState*) +{ + return jsOwnedString(m_value); +} + +double StringNode::evaluateToNumber(ExecState*) +{ + return m_value.toDouble(); +} + +bool StringNode::evaluateToBoolean(ExecState*) +{ + return !m_value.isEmpty(); +} + +// ------------------------------ RegExpNode ----------------------------------- + +JSValue* RegExpNode::evaluate(ExecState* exec) +{ + return exec->lexicalGlobalObject()->regExpConstructor()->createRegExpImp(exec, m_regExp); +} + +// ------------------------------ ThisNode ------------------------------------- + +// ECMA 11.1.1 +JSValue* ThisNode::evaluate(ExecState* exec) +{ + return exec->thisValue(); +} + +// ------------------------------ ResolveNode ---------------------------------- + +// ECMA 11.1.2 & 10.1.4 +JSValue* ResolveNode::inlineEvaluate(ExecState* exec) +{ + // Check for missed optimization opportunity. + ASSERT(!canSkipLookup(exec, m_ident)); + + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + do { + JSObject* o = *iter; + + if (o->getPropertySlot(exec, m_ident, slot)) + return slot.getValue(exec, o, m_ident); + + ++iter; + } while (iter != end); + + return throwUndefinedVariableError(exec, m_ident); +} + +JSValue* ResolveNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double ResolveNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool ResolveNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t ResolveNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t ResolveNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +void ResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) + new (this) LocalVarAccessNode(index); +} + +JSValue* LocalVarAccessNode::inlineEvaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + return exec->localStorage()[m_index].value; +} + +JSValue* LocalVarAccessNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double LocalVarAccessNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluate(exec)->toNumber(exec); +} + +bool LocalVarAccessNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluate(exec)->toBoolean(exec); +} + +int32_t LocalVarAccessNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluate(exec)->toInt32(exec); +} + +uint32_t LocalVarAccessNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluate(exec)->toUInt32(exec); +} + +// ------------------------------ ElementNode ---------------------------------- + +void ElementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_next) + nodeStack.append(m_next.get()); + ASSERT(m_node); + nodeStack.append(m_node.get()); +} + +// ECMA 11.1.4 +JSValue* ElementNode::evaluate(ExecState* exec) +{ + JSObject* array = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList()); + int length = 0; + for (ElementNode* n = this; n; n = n->m_next.get()) { + JSValue* val = n->m_node->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + length += n->m_elision; + array->put(exec, length++, val); + } + return array; +} + +// ------------------------------ ArrayNode ------------------------------------ + +void ArrayNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_element) + nodeStack.append(m_element.get()); +} + +// ECMA 11.1.4 +JSValue* ArrayNode::evaluate(ExecState* exec) +{ + JSObject* array; + int length; + + if (m_element) { + array = static_cast<JSObject*>(m_element->evaluate(exec)); + KJS_CHECKEXCEPTIONVALUE + length = m_optional ? array->get(exec, exec->propertyNames().length)->toInt32(exec) : 0; + } else { + JSValue* newArr = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList()); + array = static_cast<JSObject*>(newArr); + length = 0; + } + + if (m_optional) + array->put(exec, exec->propertyNames().length, jsNumber(m_elision + length)); + + return array; +} + +// ------------------------------ ObjectLiteralNode ---------------------------- + +void ObjectLiteralNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_list) + nodeStack.append(m_list.get()); +} + +// ECMA 11.1.5 +JSValue* ObjectLiteralNode::evaluate(ExecState* exec) +{ + if (m_list) + return m_list->evaluate(exec); + + return exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); +} + +// ------------------------------ PropertyListNode ----------------------------- + +void PropertyListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_next) + nodeStack.append(m_next.get()); + nodeStack.append(m_node.get()); +} + +// ECMA 11.1.5 +JSValue* PropertyListNode::evaluate(ExecState* exec) +{ + JSObject* obj = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); + + for (PropertyListNode* p = this; p; p = p->m_next.get()) { + JSValue* v = p->m_node->m_assign->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + switch (p->m_node->m_type) { + case PropertyNode::Getter: + ASSERT(v->isObject()); + obj->defineGetter(exec, p->m_node->name(), static_cast<JSObject* >(v)); + break; + case PropertyNode::Setter: + ASSERT(v->isObject()); + obj->defineSetter(exec, p->m_node->name(), static_cast<JSObject* >(v)); + break; + case PropertyNode::Constant: + obj->put(exec, p->m_node->name(), v); + break; + } + } + + return obj; +} + +// ------------------------------ PropertyNode ----------------------------- + +void PropertyNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_assign.get()); +} + +// ECMA 11.1.5 +JSValue* PropertyNode::evaluate(ExecState*) +{ + ASSERT(false); + return jsNull(); +} + +// ------------------------------ BracketAccessorNode -------------------------------- + +void BracketAccessorNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +// ECMA 11.2.1a +JSValue* BracketAccessorNode::inlineEvaluate(ExecState* exec) +{ + JSValue* v1 = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* v2 = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* o = v1->toObject(exec); + uint32_t i; + if (v2->getUInt32(i)) + return o->get(exec, i); + return o->get(exec, Identifier(v2->toString(exec))); +} + +JSValue* BracketAccessorNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double BracketAccessorNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool BracketAccessorNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t BracketAccessorNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t BracketAccessorNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +// ------------------------------ DotAccessorNode -------------------------------- + +void DotAccessorNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_base.get()); +} + +// ECMA 11.2.1b +JSValue* DotAccessorNode::inlineEvaluate(ExecState* exec) +{ + JSValue* v = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + return v->toObject(exec)->get(exec, m_ident); +} + +JSValue* DotAccessorNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double DotAccessorNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool DotAccessorNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t DotAccessorNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t DotAccessorNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +// ------------------------------ ArgumentListNode ----------------------------- + +void ArgumentListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_next) + nodeStack.append(m_next.get()); + ASSERT(m_expr); + nodeStack.append(m_expr.get()); +} + +// ECMA 11.2.4 +void ArgumentListNode::evaluateList(ExecState* exec, List& list) +{ + for (ArgumentListNode* n = this; n; n = n->m_next.get()) { + JSValue* v = n->m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVOID + list.append(v); + } +} + +// ------------------------------ ArgumentsNode -------------------------------- + +void ArgumentsNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_listNode) + nodeStack.append(m_listNode.get()); +} + +// ------------------------------ NewExprNode ---------------------------------- + +void NewExprNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_args) + nodeStack.append(m_args.get()); + nodeStack.append(m_expr.get()); +} + +// ECMA 11.2.2 + +JSValue* NewExprNode::inlineEvaluate(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + List argList; + if (m_args) { + m_args->evaluateList(exec, argList); + KJS_CHECKEXCEPTIONVALUE + } + + if (!v->isObject()) + return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with new.", v, m_expr.get()); + + JSObject* constr = static_cast<JSObject*>(v); + if (!constr->implementsConstruct()) + return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, m_expr.get()); + + return constr->construct(exec, argList); +} + +JSValue* NewExprNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double NewExprNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool NewExprNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t NewExprNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t NewExprNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +void FunctionCallValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_args.get()); + nodeStack.append(m_expr.get()); +} + +// ECMA 11.2.3 +JSValue* FunctionCallValueNode::evaluate(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + if (!v->isObject()) { + return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_expr.get()); + } + + JSObject* func = static_cast<JSObject*>(v); + + if (!func->implementsCall()) { + return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_expr.get()); + } + + List argList; + m_args->evaluateList(exec, argList); + KJS_CHECKEXCEPTIONVALUE + + JSObject* thisObj = exec->dynamicGlobalObject(); + + return func->call(exec, thisObj, argList); +} + +void FunctionCallResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_args.get()); + + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) + new (this) LocalVarFunctionCallNode(index); +} + +// ECMA 11.2.3 +JSValue* FunctionCallResolveNode::inlineEvaluate(ExecState* exec) +{ + // Check for missed optimization opportunity. + ASSERT(!canSkipLookup(exec, m_ident)); + + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + do { + base = *iter; + if (base->getPropertySlot(exec, m_ident, slot)) { + JSValue* v = slot.getValue(exec, base, m_ident); + KJS_CHECKEXCEPTIONVALUE + + if (!v->isObject()) + return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident); + + JSObject* func = static_cast<JSObject*>(v); + + if (!func->implementsCall()) + return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident); + + List argList; + m_args->evaluateList(exec, argList); + KJS_CHECKEXCEPTIONVALUE + + JSObject* thisObj = base; + // ECMA 11.2.3 says that in this situation the this value should be null. + // However, section 10.2.3 says that in the case where the value provided + // by the caller is null, the global object should be used. It also says + // that the section does not apply to internal functions, but for simplicity + // of implementation we use the global object anyway here. This guarantees + // that in host objects you always get a valid object for this. + if (thisObj->isActivationObject()) + thisObj = exec->dynamicGlobalObject(); + + return func->call(exec, thisObj, argList); + } + ++iter; + } while (iter != end); + + return throwUndefinedVariableError(exec, m_ident); +} + +JSValue* FunctionCallResolveNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double FunctionCallResolveNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool FunctionCallResolveNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t FunctionCallResolveNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t FunctionCallResolveNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +JSValue* LocalVarFunctionCallNode::inlineEvaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + + JSValue* v = exec->localStorage()[m_index].value; + + if (!v->isObject()) + return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident); + + JSObject* func = static_cast<JSObject*>(v); + if (!func->implementsCall()) + return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident); + + List argList; + m_args->evaluateList(exec, argList); + KJS_CHECKEXCEPTIONVALUE + + return func->call(exec, exec->dynamicGlobalObject(), argList); +} + +JSValue* LocalVarFunctionCallNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double LocalVarFunctionCallNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool LocalVarFunctionCallNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t LocalVarFunctionCallNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t LocalVarFunctionCallNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +void FunctionCallBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_args.get()); + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +// ECMA 11.2.3 +JSValue* FunctionCallBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseVal = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* subscriptVal = m_subscript->evaluate(exec); + + JSObject* baseObj = baseVal->toObject(exec); + uint32_t i; + PropertySlot slot; + + JSValue* funcVal; + if (subscriptVal->getUInt32(i)) { + if (baseObj->getPropertySlot(exec, i, slot)) + funcVal = slot.getValue(exec, baseObj, i); + else + funcVal = jsUndefined(); + } else { + Identifier ident(subscriptVal->toString(exec)); + if (baseObj->getPropertySlot(exec, ident, slot)) + funcVal = baseObj->get(exec, ident); + else + funcVal = jsUndefined(); + } + + KJS_CHECKEXCEPTIONVALUE + + if (!funcVal->isObject()) + return throwError(exec, TypeError, "Value %s (result of expression %s[%s]) is not object.", funcVal, m_base.get(), m_subscript.get()); + + JSObject* func = static_cast<JSObject*>(funcVal); + + if (!func->implementsCall()) + return throwError(exec, TypeError, "Object %s (result of expression %s[%s]) does not allow calls.", funcVal, m_base.get(), m_subscript.get()); + + List argList; + m_args->evaluateList(exec, argList); + KJS_CHECKEXCEPTIONVALUE + + JSObject* thisObj = baseObj; + ASSERT(thisObj); + ASSERT(thisObj->isObject()); + ASSERT(!thisObj->isActivationObject()); + + return func->call(exec, thisObj, argList); +} + +static const char* dotExprNotAnObjectString() KJS_FAST_CALL; +static const char* dotExprNotAnObjectString() +{ + return "Value %s (result of expression %s.%s) is not object."; +} + +static const char* dotExprDoesNotAllowCallsString() KJS_FAST_CALL; +static const char* dotExprDoesNotAllowCallsString() +{ + return "Object %s (result of expression %s.%s) does not allow calls."; +} + +void FunctionCallDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_args.get()); + nodeStack.append(m_base.get()); +} + +// ECMA 11.2.3 +JSValue* FunctionCallDotNode::inlineEvaluate(ExecState* exec) +{ + JSValue* baseVal = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* baseObj = baseVal->toObject(exec); + PropertySlot slot; + JSValue* funcVal = baseObj->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, baseObj, m_ident) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + if (!funcVal->isObject()) + return throwError(exec, TypeError, dotExprNotAnObjectString(), funcVal, m_base.get(), m_ident); + + JSObject* func = static_cast<JSObject*>(funcVal); + + if (!func->implementsCall()) + return throwError(exec, TypeError, dotExprDoesNotAllowCallsString(), funcVal, m_base.get(), m_ident); + + List argList; + m_args->evaluateList(exec, argList); + KJS_CHECKEXCEPTIONVALUE + + JSObject* thisObj = baseObj; + ASSERT(thisObj); + ASSERT(thisObj->isObject()); + ASSERT(!thisObj->isActivationObject()); + + return func->call(exec, thisObj, argList); +} + +JSValue* FunctionCallDotNode::evaluate(ExecState* exec) +{ + return inlineEvaluate(exec); +} + +double FunctionCallDotNode::evaluateToNumber(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toNumber(exec); +} + +bool FunctionCallDotNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return v->toBoolean(exec); +} + +int32_t FunctionCallDotNode::evaluateToInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toInt32(exec); +} + +uint32_t FunctionCallDotNode::evaluateToUInt32(ExecState* exec) +{ + JSValue* v = inlineEvaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + return v->toUInt32(exec); +} + +// ECMA 11.3 + +// ------------------------------ PostfixResolveNode ---------------------------------- + +// Increment +void PostIncResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) { + if (isConstant(localStorage, index)) + new (this) PostIncConstNode(index); + else + new (this) PostIncLocalVarNode(index); + } +} + +JSValue* PostIncResolveNode::evaluate(ExecState* exec) +{ + // Check for missed optimization opportunity. + ASSERT(!canSkipLookup(exec, m_ident)); + + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + do { + if ((*iter)->getPropertySlot(exec, m_ident, slot)) { + // If m_ident is 'arguments', the base->getPropertySlot() may cause + // base (which must be an ActivationImp in such this case) to be torn + // off from the activation stack, in which case we need to get it again + // from the ScopeChainIterator. + + JSObject* base = *iter; + JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec); + base->put(exec, m_ident, jsNumber(v->toNumber(exec) + 1)); + return v; + } + + ++iter; + } while (iter != end); + + return throwUndefinedVariableError(exec, m_ident); +} + +void PostIncResolveNode::optimizeForUnnecessaryResult() +{ + new (this) PreIncResolveNode(PlacementNewAdopt); +} + +JSValue* PostIncLocalVarNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + + JSValue** slot = &exec->localStorage()[m_index].value; + JSValue* v = (*slot)->toJSNumber(exec); + *slot = jsNumber(v->toNumber(exec) + 1); + return v; +} + +void PostIncLocalVarNode::optimizeForUnnecessaryResult() +{ + new (this) PreIncLocalVarNode(m_index); +} + +// Decrement +void PostDecResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) { + if (isConstant(localStorage, index)) + new (this) PostDecConstNode(index); + else + new (this) PostDecLocalVarNode(index); + } +} + +JSValue* PostDecResolveNode::evaluate(ExecState* exec) +{ + // Check for missed optimization opportunity. + ASSERT(!canSkipLookup(exec, m_ident)); + + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + do { + if ((*iter)->getPropertySlot(exec, m_ident, slot)) { + // See the comment in PostIncResolveNode::evaluate(). + + JSObject* base = *iter; + JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec); + base->put(exec, m_ident, jsNumber(v->toNumber(exec) - 1)); + return v; + } + + ++iter; + } while (iter != end); + + return throwUndefinedVariableError(exec, m_ident); +} + +void PostDecResolveNode::optimizeForUnnecessaryResult() +{ + new (this) PreDecResolveNode(PlacementNewAdopt); +} + +JSValue* PostDecLocalVarNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + + JSValue** slot = &exec->localStorage()[m_index].value; + JSValue* v = (*slot)->toJSNumber(exec); + *slot = jsNumber(v->toNumber(exec) - 1); + return v; +} + +double PostDecLocalVarNode::inlineEvaluateToNumber(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + + JSValue** slot = &exec->localStorage()[m_index].value; + double n = (*slot)->toNumber(exec); + *slot = jsNumber(n - 1); + return n; +} + +double PostDecLocalVarNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +bool PostDecLocalVarNode::evaluateToBoolean(ExecState* exec) +{ + double result = inlineEvaluateToNumber(exec); + return result > 0.0 || 0.0 > result; // NaN produces false as well +} + +int32_t PostDecLocalVarNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t PostDecLocalVarNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +void PostDecLocalVarNode::optimizeForUnnecessaryResult() +{ + new (this) PreDecLocalVarNode(m_index); +} + +// ------------------------------ PostfixBracketNode ---------------------------------- + +void PostfixBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +JSValue* PostIncBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) { + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = v->toJSNumber(exec); + base->put(exec, propertyIndex, jsNumber(v2->toNumber(exec) + 1)); + + return v2; + } + + Identifier propertyName(subscript->toString(exec)); + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = v->toJSNumber(exec); + base->put(exec, propertyName, jsNumber(v2->toNumber(exec) + 1)); + return v2; +} + +JSValue* PostDecBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) { + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = v->toJSNumber(exec); + base->put(exec, propertyIndex, jsNumber(v2->toNumber(exec) - 1)); + return v2; + } + + Identifier propertyName(subscript->toString(exec)); + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = v->toJSNumber(exec); + base->put(exec, propertyName, jsNumber(v2->toNumber(exec) - 1)); + return v2; +} + +// ------------------------------ PostfixDotNode ---------------------------------- + +void PostfixDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_base.get()); +} + +JSValue* PostIncDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* base = baseValue->toObject(exec); + + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = v->toJSNumber(exec); + base->put(exec, m_ident, jsNumber(v2->toNumber(exec) + 1)); + return v2; +} + +JSValue* PostDecDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* base = baseValue->toObject(exec); + + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = v->toJSNumber(exec); + base->put(exec, m_ident, jsNumber(v2->toNumber(exec) - 1)); + return v2; +} + +// ------------------------------ PostfixErrorNode ----------------------------------- + +JSValue* PostfixErrorNode::evaluate(ExecState* exec) +{ + throwError(exec, ReferenceError, "Postfix %s operator applied to value that is not a reference.", + m_operator == OpPlusPlus ? "++" : "--"); + handleException(exec); + return jsUndefined(); +} + +// ------------------------------ DeleteResolveNode ----------------------------------- + +void DeleteResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) + new (this) LocalVarDeleteNode(); +} + +// ECMA 11.4.1 + +JSValue* DeleteResolveNode::evaluate(ExecState* exec) +{ + // Check for missed optimization opportunity. + ASSERT(!canSkipLookup(exec, m_ident)); + + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // We must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + do { + base = *iter; + if (base->getPropertySlot(exec, m_ident, slot)) + return jsBoolean(base->deleteProperty(exec, m_ident)); + + ++iter; + } while (iter != end); + + return jsBoolean(true); +} + +JSValue* LocalVarDeleteNode::evaluate(ExecState*) +{ + return jsBoolean(false); +} + +// ------------------------------ DeleteBracketNode ----------------------------------- + +void DeleteBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +JSValue* DeleteBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) + return jsBoolean(base->deleteProperty(exec, propertyIndex)); + + Identifier propertyName(subscript->toString(exec)); + return jsBoolean(base->deleteProperty(exec, propertyName)); +} + +// ------------------------------ DeleteDotNode ----------------------------------- + +void DeleteDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_base.get()); +} + +JSValue* DeleteDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + JSObject* base = baseValue->toObject(exec); + KJS_CHECKEXCEPTIONVALUE + + return jsBoolean(base->deleteProperty(exec, m_ident)); +} + +// ------------------------------ DeleteValueNode ----------------------------------- + +void DeleteValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +JSValue* DeleteValueNode::evaluate(ExecState* exec) +{ + m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + // delete on a non-location expression ignores the value and returns true + return jsBoolean(true); +} + +// ------------------------------ VoidNode ------------------------------------- + +void VoidNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +// ECMA 11.4.2 +JSValue* VoidNode::evaluate(ExecState* exec) +{ + m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + return jsUndefined(); +} + +// ECMA 11.4.3 + +// ------------------------------ TypeOfValueNode ----------------------------------- + +void TypeOfValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +static JSValue* typeStringForValue(JSValue* v) KJS_FAST_CALL; +static JSValue* typeStringForValue(JSValue* v) +{ + switch (v->type()) { + case UndefinedType: + return jsString("undefined"); + case NullType: + return jsString("object"); + case BooleanType: + return jsString("boolean"); + case NumberType: + return jsString("number"); + case StringType: + return jsString("string"); + default: + if (v->isObject()) { + // Return "undefined" for objects that should be treated + // as null when doing comparisons. + if (static_cast<JSObject*>(v)->masqueradeAsUndefined()) + return jsString("undefined"); + else if (static_cast<JSObject*>(v)->implementsCall()) + return jsString("function"); + } + + return jsString("object"); + } +} + +void TypeOfResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) + new (this) LocalVarTypeOfNode(index); +} + +JSValue* LocalVarTypeOfNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + + return typeStringForValue(exec->localStorage()[m_index].value); +} + +JSValue* TypeOfResolveNode::evaluate(ExecState* exec) +{ + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // We must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + do { + base = *iter; + if (base->getPropertySlot(exec, m_ident, slot)) { + JSValue* v = slot.getValue(exec, base, m_ident); + return typeStringForValue(v); + } + + ++iter; + } while (iter != end); + + return jsString("undefined"); +} + +// ------------------------------ TypeOfValueNode ----------------------------------- + +JSValue* TypeOfValueNode::evaluate(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + return typeStringForValue(v); +} + +// ECMA 11.4.4 and 11.4.5 + +// ------------------------------ PrefixResolveNode ---------------------------------- + +void PreIncResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) { + if (isConstant(localStorage, index)) + new (this) PreIncConstNode(index); + else + new (this) PreIncLocalVarNode(index); + } +} + +JSValue* PreIncLocalVarNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + JSValue** slot = &exec->localStorage()[m_index].value; + + double n = (*slot)->toNumber(exec); + JSValue* n2 = jsNumber(n + 1); + *slot = n2; + return n2; +} + +JSValue* PreIncResolveNode::evaluate(ExecState* exec) +{ + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + do { + if ((*iter)->getPropertySlot(exec, m_ident, slot)) { + // See the comment in PostIncResolveNode::evaluate(). + + JSObject* base = *iter; + JSValue* v = slot.getValue(exec, base, m_ident); + + double n = v->toNumber(exec); + JSValue* n2 = jsNumber(n + 1); + base->put(exec, m_ident, n2); + + return n2; + } + + ++iter; + } while (iter != end); + + return throwUndefinedVariableError(exec, m_ident); +} + +void PreDecResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) +{ + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) { + if (isConstant(localStorage, index)) + new (this) PreDecConstNode(index); + else + new (this) PreDecLocalVarNode(index); + } +} + +JSValue* PreDecLocalVarNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + JSValue** slot = &exec->localStorage()[m_index].value; + + double n = (*slot)->toNumber(exec); + JSValue* n2 = jsNumber(n - 1); + *slot = n2; + return n2; +} + +JSValue* PreDecResolveNode::evaluate(ExecState* exec) +{ + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + do { + if ((*iter)->getPropertySlot(exec, m_ident, slot)) { + // See the comment in PostIncResolveNode::evaluate(). + + JSObject* base = *iter; + JSValue* v = slot.getValue(exec, base, m_ident); + + double n = v->toNumber(exec); + JSValue* n2 = jsNumber(n - 1); + base->put(exec, m_ident, n2); + + return n2; + } + + ++iter; + } while (iter != end); + + return throwUndefinedVariableError(exec, m_ident); +} + +// ------------------------------ PreIncConstNode ---------------------------------- + +JSValue* PreIncConstNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + return jsNumber(exec->localStorage()[m_index].value->toNumber(exec) + 1); +} + +// ------------------------------ PreDecConstNode ---------------------------------- + +JSValue* PreDecConstNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + return jsNumber(exec->localStorage()[m_index].value->toNumber(exec) - 1); +} + +// ------------------------------ PostIncConstNode ---------------------------------- + +JSValue* PostIncConstNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + return jsNumber(exec->localStorage()[m_index].value->toNumber(exec)); +} + +// ------------------------------ PostDecConstNode ---------------------------------- + +JSValue* PostDecConstNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + return jsNumber(exec->localStorage()[m_index].value->toNumber(exec)); +} + +// ------------------------------ PrefixBracketNode ---------------------------------- + +void PrefixBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +JSValue* PreIncBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) { + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* n2 = jsNumber(v->toNumber(exec) + 1); + base->put(exec, propertyIndex, n2); + + return n2; + } + + Identifier propertyName(subscript->toString(exec)); + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* n2 = jsNumber(v->toNumber(exec) + 1); + base->put(exec, propertyName, n2); + + return n2; +} + +JSValue* PreDecBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) { + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* n2 = jsNumber(v->toNumber(exec) - 1); + base->put(exec, propertyIndex, n2); + + return n2; + } + + Identifier propertyName(subscript->toString(exec)); + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + JSValue* n2 = jsNumber(v->toNumber(exec) - 1); + base->put(exec, propertyName, n2); + + return n2; +} + +// ------------------------------ PrefixDotNode ---------------------------------- + +void PrefixDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_base.get()); +} + +JSValue* PreIncDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* base = baseValue->toObject(exec); + + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + double n = v->toNumber(exec); + JSValue* n2 = jsNumber(n + 1); + base->put(exec, m_ident, n2); + + return n2; +} + +JSValue* PreDecDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* base = baseValue->toObject(exec); + + PropertySlot slot; + JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + + double n = v->toNumber(exec); + JSValue* n2 = jsNumber(n - 1); + base->put(exec, m_ident, n2); + + return n2; +} + +// ------------------------------ PrefixErrorNode ----------------------------------- + +JSValue* PrefixErrorNode::evaluate(ExecState* exec) +{ + throwError(exec, ReferenceError, "Prefix %s operator applied to value that is not a reference.", + m_operator == OpPlusPlus ? "++" : "--"); + handleException(exec); + return jsUndefined(); +} + +// ------------------------------ UnaryPlusNode -------------------------------- + +void UnaryPlusNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +// ECMA 11.4.6 +JSValue* UnaryPlusNode::evaluate(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + return v->toJSNumber(exec); +} + +bool UnaryPlusNode::evaluateToBoolean(ExecState* exec) +{ + return m_expr->evaluateToBoolean(exec); +} + +double UnaryPlusNode::evaluateToNumber(ExecState* exec) +{ + return m_expr->evaluateToNumber(exec); +} + +int32_t UnaryPlusNode::evaluateToInt32(ExecState* exec) +{ + return m_expr->evaluateToInt32(exec); +} + +uint32_t UnaryPlusNode::evaluateToUInt32(ExecState* exec) +{ + return m_expr->evaluateToInt32(exec); +} + +// ------------------------------ NegateNode ----------------------------------- + +void NegateNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +// ECMA 11.4.7 +JSValue* NegateNode::evaluate(ExecState* exec) +{ + // No need to check exception, caller will do so right after evaluate() + return jsNumber(-m_expr->evaluateToNumber(exec)); +} + +double NegateNode::evaluateToNumber(ExecState* exec) +{ + // No need to check exception, caller will do so right after evaluateToNumber() + return -m_expr->evaluateToNumber(exec); +} + +// ------------------------------ BitwiseNotNode ------------------------------- + +void BitwiseNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +// ECMA 11.4.8 +int32_t BitwiseNotNode::inlineEvaluateToInt32(ExecState* exec) +{ + return ~m_expr->evaluateToInt32(exec); +} + +JSValue* BitwiseNotNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToInt32(exec)); +} + +double BitwiseNotNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +bool BitwiseNotNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +int32_t BitwiseNotNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +uint32_t BitwiseNotNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +// ------------------------------ LogicalNotNode ------------------------------- + +void LogicalNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +// ECMA 11.4.9 +JSValue* LogicalNotNode::evaluate(ExecState* exec) +{ + return jsBoolean(!m_expr->evaluateToBoolean(exec)); +} + +bool LogicalNotNode::evaluateToBoolean(ExecState* exec) +{ + return !m_expr->evaluateToBoolean(exec); +} + +// ------------------------------ Multiplicative Nodes ----------------------------------- + +void MultNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.5.1 +double MultNode::inlineEvaluateToNumber(ExecState* exec) +{ + double n1 = m_term1->evaluateToNumber(exec); + KJS_CHECKEXCEPTIONNUMBER + double n2 = m_term2->evaluateToNumber(exec); + return n1 * n2; +} + +JSValue* MultNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToNumber(exec)); +} + +double MultNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +bool MultNode::evaluateToBoolean(ExecState* exec) +{ + double result = inlineEvaluateToNumber(exec); + return result > 0.0 || 0.0 > result; // NaN produces false as well +} + +int32_t MultNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t MultNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +void DivNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.5.2 +double DivNode::inlineEvaluateToNumber(ExecState* exec) +{ + double n1 = m_term1->evaluateToNumber(exec); + KJS_CHECKEXCEPTIONNUMBER + double n2 = m_term2->evaluateToNumber(exec); + return n1 / n2; +} + +JSValue* DivNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToNumber(exec)); +} + +double DivNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +int32_t DivNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t DivNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +void ModNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.5.3 +double ModNode::inlineEvaluateToNumber(ExecState* exec) +{ + double n1 = m_term1->evaluateToNumber(exec); + KJS_CHECKEXCEPTIONNUMBER + double n2 = m_term2->evaluateToNumber(exec); + return fmod(n1, n2); +} + +JSValue* ModNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToNumber(exec)); +} + +double ModNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +bool ModNode::evaluateToBoolean(ExecState* exec) +{ + double result = inlineEvaluateToNumber(exec); + return result > 0.0 || 0.0 > result; // NaN produces false as well +} + +int32_t ModNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t ModNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +// ------------------------------ Additive Nodes -------------------------------------- + +static JSValue* throwOutOfMemoryError(ExecState* exec) +{ + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + return error; +} + +static double throwOutOfMemoryErrorToNumber(ExecState* exec) +{ + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + return 0.0; +} + +// ECMA 11.6 +static JSValue* addSlowCase(ExecState* exec, JSValue* v1, JSValue* v2) +{ + // exception for the Date exception in defaultValue() + JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType); + JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType); + + if (p1->isString() || p2->isString()) { + UString value = p1->toString(exec) + p2->toString(exec); + if (value.isNull()) + return throwOutOfMemoryError(exec); + return jsString(value); + } + + return jsNumber(p1->toNumber(exec) + p2->toNumber(exec)); +} + +static double addSlowCaseToNumber(ExecState* exec, JSValue* v1, JSValue* v2) +{ + // exception for the Date exception in defaultValue() + JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType); + JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType); + + if (p1->isString() || p2->isString()) { + UString value = p1->toString(exec) + p2->toString(exec); + if (value.isNull()) + return throwOutOfMemoryErrorToNumber(exec); + return value.toDouble(); + } + + return p1->toNumber(exec) + p2->toNumber(exec); +} + +// Fast-path choices here are based on frequency data from SunSpider: +// <times> Add case: <t1> <t2> +// --------------------------- +// 5627160 Add case: 1 1 +// 247427 Add case: 5 5 +// 20901 Add case: 5 6 +// 13978 Add case: 5 1 +// 4000 Add case: 1 5 +// 1 Add case: 3 5 + +static inline JSValue* add(ExecState* exec, JSValue* v1, JSValue* v2) +{ + JSType t1 = v1->type(); + JSType t2 = v2->type(); + const unsigned bothTypes = (t1 << 3) | t2; + + if (bothTypes == ((NumberType << 3) | NumberType)) + return jsNumber(v1->toNumber(exec) + v2->toNumber(exec)); + if (bothTypes == ((StringType << 3) | StringType)) { + UString value = static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value(); + if (value.isNull()) + return throwOutOfMemoryError(exec); + return jsString(value); + } + + // All other cases are pretty uncommon + return addSlowCase(exec, v1, v2); +} + +static inline double addToNumber(ExecState* exec, JSValue* v1, JSValue* v2) +{ + JSType t1 = v1->type(); + JSType t2 = v2->type(); + const unsigned bothTypes = (t1 << 3) | t2; + + if (bothTypes == ((NumberType << 3) | NumberType)) + return v1->toNumber(exec) + v2->toNumber(exec); + if (bothTypes == ((StringType << 3) | StringType)) { + UString value = static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value(); + if (value.isNull()) + return throwOutOfMemoryErrorToNumber(exec); + return value.toDouble(); + } + + // All other cases are pretty uncommon + return addSlowCaseToNumber(exec, v1, v2); +} + +void AddNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.6.1 +JSValue* AddNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_term1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = m_term2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + return add(exec, v1, v2); +} + +double AddNode::inlineEvaluateToNumber(ExecState* exec) +{ + JSValue* v1 = m_term1->evaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + + JSValue* v2 = m_term2->evaluate(exec); + KJS_CHECKEXCEPTIONNUMBER + + return addToNumber(exec, v1, v2); +} + +double AddNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +int32_t AddNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t AddNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +double AddNumbersNode::inlineEvaluateToNumber(ExecState* exec) +{ + double n1 = m_term1->evaluateToNumber(exec); + KJS_CHECKEXCEPTIONNUMBER + double n2 = m_term2->evaluateToNumber(exec); + return n1 + n2; +} + +JSValue* AddNumbersNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToNumber(exec)); +} + +double AddNumbersNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +int32_t AddNumbersNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t AddNumbersNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +JSValue* AddStringsNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_term1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = m_term2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + return jsString(static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value()); +} + +JSValue* AddStringLeftNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_term1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = m_term2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType); + return jsString(static_cast<StringImp*>(v1)->value() + p2->toString(exec)); +} + +JSValue* AddStringRightNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_term1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* v2 = m_term2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType); + return jsString(p1->toString(exec) + static_cast<StringImp*>(v2)->value()); +} + +void SubNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.6.2 +double SubNode::inlineEvaluateToNumber(ExecState* exec) +{ + double n1 = m_term1->evaluateToNumber(exec); + KJS_CHECKEXCEPTIONNUMBER + double n2 = m_term2->evaluateToNumber(exec); + return n1 - n2; +} + +JSValue* SubNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToNumber(exec)); +} + +double SubNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToNumber(exec); +} + +int32_t SubNode::evaluateToInt32(ExecState* exec) +{ + return JSValue::toInt32(inlineEvaluateToNumber(exec)); +} + +uint32_t SubNode::evaluateToUInt32(ExecState* exec) +{ + return JSValue::toUInt32(inlineEvaluateToNumber(exec)); +} + +// ------------------------------ Shift Nodes ------------------------------------ + +void LeftShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.7.1 +int32_t LeftShiftNode::inlineEvaluateToInt32(ExecState* exec) +{ + int i1 = m_term1->evaluateToInt32(exec); + KJS_CHECKEXCEPTIONNUMBER + unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f; + return (i1 << i2); +} + +JSValue* LeftShiftNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToInt32(exec)); +} + +double LeftShiftNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +int32_t LeftShiftNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +uint32_t LeftShiftNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +void RightShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.7.2 +int32_t RightShiftNode::inlineEvaluateToInt32(ExecState* exec) +{ + int i1 = m_term1->evaluateToInt32(exec); + KJS_CHECKEXCEPTIONNUMBER + unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f; + return (i1 >> i2); +} + +JSValue* RightShiftNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToInt32(exec)); +} + +double RightShiftNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +int32_t RightShiftNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +uint32_t RightShiftNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +void UnsignedRightShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_term1.get()); + nodeStack.append(m_term2.get()); +} + +// ECMA 11.7.3 +uint32_t UnsignedRightShiftNode::inlineEvaluateToUInt32(ExecState* exec) +{ + unsigned int i1 = m_term1->evaluateToUInt32(exec); + KJS_CHECKEXCEPTIONNUMBER + unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f; + return (i1 >> i2); +} + +JSValue* UnsignedRightShiftNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToUInt32(exec)); +} + +double UnsignedRightShiftNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToUInt32(exec); +} + +int32_t UnsignedRightShiftNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToUInt32(exec); +} + +uint32_t UnsignedRightShiftNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToUInt32(exec); +} + +// ------------------------------ Relational Nodes ------------------------------- + +static inline bool lessThan(ExecState* exec, JSValue* v1, JSValue* v2) +{ + double n1; + double n2; + JSValue* p1; + JSValue* p2; + bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1); + bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2); + + if (wasNotString1 | wasNotString2) + return n1 < n2; + + return static_cast<const StringImp*>(p1)->value() < static_cast<const StringImp*>(p2)->value(); +} + +static inline bool lessThanEq(ExecState* exec, JSValue* v1, JSValue* v2) +{ + double n1; + double n2; + JSValue* p1; + JSValue* p2; + bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1); + bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2); + + if (wasNotString1 | wasNotString2) + return n1 <= n2; + + return !(static_cast<const StringImp*>(p2)->value() < static_cast<const StringImp*>(p1)->value()); +} + +void LessNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.8.1 +bool LessNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return lessThan(exec, v1, v2); +} + +JSValue* LessNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool LessNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +bool LessNumbersNode::inlineEvaluateToBoolean(ExecState* exec) +{ + double n1 = m_expr1->evaluateToNumber(exec); + KJS_CHECKEXCEPTIONVALUE + double n2 = m_expr2->evaluateToNumber(exec); + return n1 < n2; +} + +JSValue* LessNumbersNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool LessNumbersNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +bool LessStringsNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* v2 = m_expr2->evaluate(exec); + return static_cast<StringImp*>(v1)->value() < static_cast<StringImp*>(v2)->value(); +} + +JSValue* LessStringsNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool LessStringsNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void GreaterNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.8.2 +bool GreaterNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return lessThan(exec, v2, v1); +} + +JSValue* GreaterNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool GreaterNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void LessEqNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.8.3 +bool LessEqNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return lessThanEq(exec, v1, v2); +} + +JSValue* LessEqNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool LessEqNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void GreaterEqNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.8.4 +bool GreaterEqNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return lessThanEq(exec, v2, v1); +} + +JSValue* GreaterEqNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool GreaterEqNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void InstanceOfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.8.6 +JSValue* InstanceOfNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + if (!v2->isObject()) + return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, m_expr2.get()); + + JSObject* o2 = static_cast<JSObject*>(v2); + + // According to the spec, only some types of objects "implement" the [[HasInstance]] property. + // But we are supposed to throw an exception where the object does not "have" the [[HasInstance]] + // property. It seems that all objects have the property, but not all implement it, so in this + // case we return false (consistent with Mozilla). + if (!o2->implementsHasInstance()) + return jsBoolean(false); + + return jsBoolean(o2->hasInstance(exec, v1)); +} + +bool InstanceOfNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + + if (!v2->isObject()) { + throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'instanceof' operator.", v2, m_expr2.get()); + return false; + } + + JSObject* o2 = static_cast<JSObject*>(v2); + + // According to the spec, only some types of objects "implement" the [[HasInstance]] property. + // But we are supposed to throw an exception where the object does not "have" the [[HasInstance]] + // property. It seems that all objects have the property, but not all implement it, so in this + // case we return false (consistent with Mozilla). + if (!o2->implementsHasInstance()) + return false; + + return o2->hasInstance(exec, v1); +} + +void InNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.8.7 +JSValue* InNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + if (!v2->isObject()) + return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'in' operator.", v2, m_expr2.get()); + + return jsBoolean(static_cast<JSObject*>(v2)->hasProperty(exec, Identifier(v1->toString(exec)))); +} + +bool InNode::evaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + + if (!v2->isObject()) { + throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'in' operator.", v2, m_expr2.get()); + return false; + } + + return static_cast<JSObject*>(v2)->hasProperty(exec, Identifier(v1->toString(exec))); +} + +// ------------------------------ Equality Nodes ------------------------------------ + +void EqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.9.1 +bool EqualNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + + return equal(exec, v1, v2); +} + +JSValue* EqualNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool EqualNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void NotEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.9.2 +bool NotEqualNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + + return !equal(exec,v1, v2); +} + +JSValue* NotEqualNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool NotEqualNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void StrictEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.9.4 +bool StrictEqualNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + + return strictEqual(exec,v1, v2); +} + +JSValue* StrictEqualNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool StrictEqualNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +void NotStrictEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.9.5 +bool NotStrictEqualNode::inlineEvaluateToBoolean(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONBOOLEAN + + return !strictEqual(exec,v1, v2); +} + +JSValue* NotStrictEqualNode::evaluate(ExecState* exec) +{ + return jsBoolean(inlineEvaluateToBoolean(exec)); +} + +bool NotStrictEqualNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToBoolean(exec); +} + +// ------------------------------ Bit Operation Nodes ---------------------------------- + +void BitAndNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.10 +JSValue* BitAndNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + return jsNumberFromAnd(exec, v1, v2); +} + +int32_t BitAndNode::inlineEvaluateToInt32(ExecState* exec) +{ + int32_t i1 = m_expr1->evaluateToInt32(exec); + KJS_CHECKEXCEPTIONNUMBER + int32_t i2 = m_expr2->evaluateToInt32(exec); + return (i1 & i2); +} + +double BitAndNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +bool BitAndNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +int32_t BitAndNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +uint32_t BitAndNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +void BitXOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +int32_t BitXOrNode::inlineEvaluateToInt32(ExecState* exec) +{ + int i1 = m_expr1->evaluateToInt32(exec); + KJS_CHECKEXCEPTIONNUMBER + int i2 = m_expr2->evaluateToInt32(exec); + return (i1 ^ i2); +} + +JSValue* BitXOrNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToInt32(exec)); +} + +double BitXOrNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +bool BitXOrNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +int32_t BitXOrNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +uint32_t BitXOrNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +void BitOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +int32_t BitOrNode::inlineEvaluateToInt32(ExecState* exec) +{ + int i1 = m_expr1->evaluateToInt32(exec); + KJS_CHECKEXCEPTIONNUMBER + int i2 = m_expr2->evaluateToInt32(exec); + return (i1 | i2); +} + +JSValue* BitOrNode::evaluate(ExecState* exec) +{ + return jsNumber(inlineEvaluateToInt32(exec)); +} + +double BitOrNode::evaluateToNumber(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +bool BitOrNode::evaluateToBoolean(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +int32_t BitOrNode::evaluateToInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +uint32_t BitOrNode::evaluateToUInt32(ExecState* exec) +{ + return inlineEvaluateToInt32(exec); +} + +// ------------------------------ Binary Logical Nodes ---------------------------- + +void LogicalAndNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.11 +JSValue* LogicalAndNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + bool b1 = v1->toBoolean(exec); + KJS_CHECKEXCEPTIONVALUE + if (!b1) + return v1; + JSValue* v2 = m_expr2->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + return v2; +} + +bool LogicalAndNode::evaluateToBoolean(ExecState* exec) +{ + bool b = m_expr1->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return b && m_expr2->evaluateToBoolean(exec); +} + +void LogicalOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +JSValue* LogicalOrNode::evaluate(ExecState* exec) +{ + JSValue* v1 = m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + if (v1->toBoolean(exec)) + return v1; + return m_expr2->evaluate(exec); +} + +bool LogicalOrNode::evaluateToBoolean(ExecState* exec) +{ + bool b = m_expr1->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return b || m_expr2->evaluateToBoolean(exec); +} + +// ------------------------------ ConditionalNode ------------------------------ + +void ConditionalNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); + nodeStack.append(m_logical.get()); +} + +// ECMA 11.12 +JSValue* ConditionalNode::evaluate(ExecState* exec) +{ + bool b = m_logical->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONVALUE + return b ? m_expr1->evaluate(exec) : m_expr2->evaluate(exec); +} + +bool ConditionalNode::evaluateToBoolean(ExecState* exec) +{ + bool b = m_logical->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONBOOLEAN + return b ? m_expr1->evaluateToBoolean(exec) : m_expr2->evaluateToBoolean(exec); +} + +double ConditionalNode::evaluateToNumber(ExecState* exec) +{ + bool b = m_logical->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONNUMBER + return b ? m_expr1->evaluateToNumber(exec) : m_expr2->evaluateToNumber(exec); +} + +int32_t ConditionalNode::evaluateToInt32(ExecState* exec) +{ + bool b = m_logical->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONNUMBER + return b ? m_expr1->evaluateToInt32(exec) : m_expr2->evaluateToInt32(exec); +} + +uint32_t ConditionalNode::evaluateToUInt32(ExecState* exec) +{ + bool b = m_logical->evaluateToBoolean(exec); + KJS_CHECKEXCEPTIONNUMBER + return b ? m_expr1->evaluateToUInt32(exec) : m_expr2->evaluateToUInt32(exec); +} + +// ECMA 11.13 + +static ALWAYS_INLINE JSValue* valueForReadModifyAssignment(ExecState* exec, JSValue* current, ExpressionNode* right, Operator oper) KJS_FAST_CALL; +static ALWAYS_INLINE JSValue* valueForReadModifyAssignment(ExecState* exec, JSValue* current, ExpressionNode* right, Operator oper) +{ + JSValue* v; + int i1; + int i2; + unsigned int ui; + switch (oper) { + case OpMultEq: + v = jsNumber(current->toNumber(exec) * right->evaluateToNumber(exec)); + break; + case OpDivEq: + v = jsNumber(current->toNumber(exec) / right->evaluateToNumber(exec)); + break; + case OpPlusEq: + v = add(exec, current, right->evaluate(exec)); + break; + case OpMinusEq: + v = jsNumber(current->toNumber(exec) - right->evaluateToNumber(exec)); + break; + case OpLShift: + i1 = current->toInt32(exec); + i2 = right->evaluateToInt32(exec); + v = jsNumber(i1 << i2); + break; + case OpRShift: + i1 = current->toInt32(exec); + i2 = right->evaluateToInt32(exec); + v = jsNumber(i1 >> i2); + break; + case OpURShift: + ui = current->toUInt32(exec); + i2 = right->evaluateToInt32(exec); + v = jsNumber(ui >> i2); + break; + case OpAndEq: + i1 = current->toInt32(exec); + i2 = right->evaluateToInt32(exec); + v = jsNumber(i1 & i2); + break; + case OpXOrEq: + i1 = current->toInt32(exec); + i2 = right->evaluateToInt32(exec); + v = jsNumber(i1 ^ i2); + break; + case OpOrEq: + i1 = current->toInt32(exec); + i2 = right->evaluateToInt32(exec); + v = jsNumber(i1 | i2); + break; + case OpModEq: { + double d1 = current->toNumber(exec); + double d2 = right->evaluateToNumber(exec); + v = jsNumber(fmod(d1, d2)); + } + break; + default: + ASSERT_NOT_REACHED(); + v = jsUndefined(); + } + + return v; +} + +// ------------------------------ ReadModifyResolveNode ----------------------------------- + +void ReadModifyResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) +{ + nodeStack.append(m_right.get()); + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) { + if (isConstant(localStorage, index)) + new (this) ReadModifyConstNode(index); + else + new (this) ReadModifyLocalVarNode(index); + } +} + +// ------------------------------ AssignResolveNode ----------------------------------- + +void AssignResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) +{ + nodeStack.append(m_right.get()); + size_t index = symbolTable.get(m_ident.ustring().rep()); + if (index != missingSymbolMarker()) { + if (isConstant(localStorage, index)) + new (this) AssignConstNode; + else + new (this) AssignLocalVarNode(index); + } +} + +// ------------------------------ ReadModifyLocalVarNode ----------------------------------- + +JSValue* ReadModifyLocalVarNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + + ASSERT(m_operator != OpEqual); + JSValue* v = valueForReadModifyAssignment(exec, exec->localStorage()[m_index].value, m_right.get(), m_operator); + + KJS_CHECKEXCEPTIONVALUE + + // We can't store a pointer into localStorage() and use it throughout the function + // body, because valueForReadModifyAssignment() might cause an ActivationImp tear-off, + // changing the value of localStorage(). + + exec->localStorage()[m_index].value = v; + return v; +} + +// ------------------------------ AssignLocalVarNode ----------------------------------- + +JSValue* AssignLocalVarNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + JSValue* v = m_right->evaluate(exec); + + KJS_CHECKEXCEPTIONVALUE + + exec->localStorage()[m_index].value = v; + + return v; +} + +// ------------------------------ ReadModifyConstNode ----------------------------------- + +JSValue* ReadModifyConstNode::evaluate(ExecState* exec) +{ + ASSERT(exec->variableObject() == exec->scopeChain().top()); + JSValue* left = exec->localStorage()[m_index].value; + ASSERT(m_operator != OpEqual); + JSValue* result = valueForReadModifyAssignment(exec, left, m_right.get(), m_operator); + KJS_CHECKEXCEPTIONVALUE + return result; +} + +// ------------------------------ AssignConstNode ----------------------------------- + +JSValue* AssignConstNode::evaluate(ExecState* exec) +{ + return m_right->evaluate(exec); +} + +JSValue* ReadModifyResolveNode::evaluate(ExecState* exec) +{ + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // We must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + do { + base = *iter; + if (base->getPropertySlot(exec, m_ident, slot)) { + // See the comment in PostIncResolveNode::evaluate(). + + base = *iter; + goto found; + } + + ++iter; + } while (iter != end); + + ASSERT(m_operator != OpEqual); + return throwUndefinedVariableError(exec, m_ident); + +found: + JSValue* v; + + ASSERT(m_operator != OpEqual); + JSValue* v1 = slot.getValue(exec, base, m_ident); + KJS_CHECKEXCEPTIONVALUE + v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); + + KJS_CHECKEXCEPTIONVALUE + + // Since valueForReadModifyAssignment() might cause an ActivationImp tear-off, + // we need to get the base from the ScopeChainIterator again. + + (*iter)->put(exec, m_ident, v); + return v; +} + +JSValue* AssignResolveNode::evaluate(ExecState* exec) +{ + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + do { + base = *iter; + if (base->getPropertySlot(exec, m_ident, slot)) { + // See the comment in PostIncResolveNode::evaluate(). + + base = *iter; + goto found; + } + + ++iter; + } while (iter != end); + +found: + JSValue* v = m_right->evaluate(exec); + + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, m_ident, v); + return v; +} + +// ------------------------------ ReadModifyDotNode ----------------------------------- + +void AssignDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_right.get()); + nodeStack.append(m_base.get()); +} + +JSValue* AssignDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* base = baseValue->toObject(exec); + + JSValue* v = m_right->evaluate(exec); + + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, m_ident, v); + return v; +} + +void ReadModifyDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_right.get()); + nodeStack.append(m_base.get()); +} + +JSValue* ReadModifyDotNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSObject* base = baseValue->toObject(exec); + + JSValue* v; + + ASSERT(m_operator != OpEqual); + PropertySlot slot; + JSValue* v1 = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); + + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, m_ident, v); + return v; +} + +// ------------------------------ AssignErrorNode ----------------------------------- + +JSValue* AssignErrorNode::evaluate(ExecState* exec) +{ + throwError(exec, ReferenceError, "Left side of assignment is not a reference."); + handleException(exec); + return jsUndefined(); +} + +// ------------------------------ AssignBracketNode ----------------------------------- + +void AssignBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_right.get()); + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +JSValue* AssignBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) { + JSValue* v = m_right->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, propertyIndex, v); + return v; + } + + Identifier propertyName(subscript->toString(exec)); + JSValue* v = m_right->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, propertyName, v); + return v; +} +void ReadModifyBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_right.get()); + nodeStack.append(m_subscript.get()); + nodeStack.append(m_base.get()); +} + +JSValue* ReadModifyBracketNode::evaluate(ExecState* exec) +{ + JSValue* baseValue = m_base->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + JSValue* subscript = m_subscript->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + JSObject* base = baseValue->toObject(exec); + + uint32_t propertyIndex; + if (subscript->getUInt32(propertyIndex)) { + JSValue* v; + ASSERT(m_operator != OpEqual); + PropertySlot slot; + JSValue* v1 = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); + + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, propertyIndex, v); + return v; + } + + Identifier propertyName(subscript->toString(exec)); + JSValue* v; + + ASSERT(m_operator != OpEqual); + PropertySlot slot; + JSValue* v1 = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); + KJS_CHECKEXCEPTIONVALUE + v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); + + KJS_CHECKEXCEPTIONVALUE + + base->put(exec, propertyName, v); + return v; +} + +// ------------------------------ CommaNode ------------------------------------ + +void CommaNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 11.14 +JSValue* CommaNode::evaluate(ExecState* exec) +{ + m_expr1->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + return m_expr2->evaluate(exec); +} + +// ------------------------------ ConstDeclNode ---------------------------------- + +ConstDeclNode::ConstDeclNode(const Identifier& ident, ExpressionNode* init) + : m_ident(ident) + , m_init(init) +{ +} + +void ConstDeclNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_next) + nodeStack.append(m_next.get()); + if (m_init) + nodeStack.append(m_init.get()); +} + +void ConstDeclNode::handleSlowCase(ExecState* exec, const ScopeChain& chain, JSValue* val) +{ + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // We must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + + do { + base = *iter; + if (base->getPropertySlot(exec, m_ident, slot)) + break; + ++iter; + } while (iter != end); + + ASSERT(base->isActivationObject() || base->isGlobalObject()); + + static_cast<JSVariableObject*>(base)->initializeVariable(exec, m_ident, val, ReadOnly); +} + +// ECMA 12.2 +inline void ConstDeclNode::evaluateSingle(ExecState* exec) +{ + ASSERT(exec->variableObject()->hasOwnProperty(exec, m_ident) || exec->codeType() == EvalCode); // Guaranteed by processDeclarations. + const ScopeChain& chain = exec->scopeChain(); + JSVariableObject* variableObject = exec->variableObject(); + + ASSERT(!chain.isEmpty()); + + bool inGlobalScope = ++chain.begin() == chain.end(); + + if (m_init) { + if (inGlobalScope) { + JSValue* val = m_init->evaluate(exec); + unsigned attributes = ReadOnly; + if (exec->codeType() != EvalCode) + attributes |= DontDelete; + variableObject->initializeVariable(exec, m_ident, val, attributes); + } else { + JSValue* val = m_init->evaluate(exec); + KJS_CHECKEXCEPTIONVOID + + // if the variable object is the top of the scope chain, then that must + // be where this variable is declared, processVarDecls would have put + // it there. Don't search the scope chain, to optimize this very common case. + if (chain.top() != variableObject) + return handleSlowCase(exec, chain, val); + + variableObject->initializeVariable(exec, m_ident, val, ReadOnly); + } + } +} + +JSValue* ConstDeclNode::evaluate(ExecState* exec) +{ + evaluateSingle(exec); + + if (ConstDeclNode* n = m_next.get()) { + do { + n->evaluateSingle(exec); + KJS_CHECKEXCEPTIONVALUE + n = n->m_next.get(); + } while (n); + } + return jsUndefined(); +} + +// ------------------------------ ConstStatementNode ----------------------------- + +void ConstStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + ASSERT(m_next); + nodeStack.append(m_next.get()); +} + +// ECMA 12.2 +JSValue* ConstStatementNode::execute(ExecState* exec) +{ + m_next->evaluate(exec); + KJS_CHECKEXCEPTION + + return exec->setNormalCompletion(); +} + +// ------------------------------ Helper functions for handling Vectors of StatementNode ------------------------------- + +static inline void statementListPushFIFO(StatementVector& statements, DeclarationStacks::NodeStack& stack) +{ + StatementVector::iterator it = statements.end(); + StatementVector::iterator begin = statements.begin(); + while (it != begin) { + --it; + stack.append((*it).get()); + } +} + +static inline Node* statementListInitializeVariableAccessStack(StatementVector& statements, DeclarationStacks::NodeStack& stack) +{ + if (statements.isEmpty()) + return 0; + + StatementVector::iterator it = statements.end(); + StatementVector::iterator begin = statements.begin(); + StatementVector::iterator beginPlusOne = begin + 1; + + while (it != beginPlusOne) { + --it; + stack.append((*it).get()); + } + + return (*begin).get(); +} + +static inline JSValue* statementListExecute(StatementVector& statements, ExecState* exec) +{ + JSValue* value = 0; + size_t size = statements.size(); + for (size_t i = 0; i != size; ++i) { + JSValue* statementValue = statements[i]->execute(exec); + if (statementValue) + value = statementValue; + if (exec->completionType() != Normal) + return value; + } + return exec->setNormalCompletion(value); +} + +// ------------------------------ BlockNode ------------------------------------ + +BlockNode::BlockNode(SourceElements* children) +{ + if (children) + children->releaseContentsIntoVector(m_children); +} + +void BlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + statementListPushFIFO(m_children, nodeStack); +} + +// ECMA 12.1 +JSValue* BlockNode::execute(ExecState* exec) +{ + return statementListExecute(m_children, exec); +} + +// ------------------------------ EmptyStatementNode --------------------------- + +// ECMA 12.3 +JSValue* EmptyStatementNode::execute(ExecState* exec) +{ + return exec->setNormalCompletion(); +} + +// ------------------------------ ExprStatementNode ---------------------------- + +void ExprStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + ASSERT(m_expr); + nodeStack.append(m_expr.get()); +} + +// ECMA 12.4 +JSValue* ExprStatementNode::execute(ExecState* exec) +{ + JSValue* value = m_expr->evaluate(exec); + KJS_CHECKEXCEPTION + + return exec->setNormalCompletion(value); +} + +// ------------------------------ VarStatementNode ---------------------------- + +void VarStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + ASSERT(m_expr); + nodeStack.append(m_expr.get()); +} + +JSValue* VarStatementNode::execute(ExecState* exec) +{ + m_expr->evaluate(exec); + KJS_CHECKEXCEPTION + + return exec->setNormalCompletion(); +} + +// ------------------------------ IfNode --------------------------------------- + +void IfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_ifBlock.get()); + nodeStack.append(m_condition.get()); +} + +// ECMA 12.5 +JSValue* IfNode::execute(ExecState* exec) +{ + bool b = m_condition->evaluateToBoolean(exec); + KJS_CHECKEXCEPTION + + if (b) + return m_ifBlock->execute(exec); + return exec->setNormalCompletion(); +} + +void IfElseNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) +{ + nodeStack.append(m_elseBlock.get()); + IfNode::optimizeVariableAccess(symbolTable, localStorage, nodeStack); +} + +// ECMA 12.5 +JSValue* IfElseNode::execute(ExecState* exec) +{ + bool b = m_condition->evaluateToBoolean(exec); + KJS_CHECKEXCEPTION + + if (b) + return m_ifBlock->execute(exec); + return m_elseBlock->execute(exec); +} + +// ------------------------------ DoWhileNode ---------------------------------- + +void DoWhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_statement.get()); + nodeStack.append(m_expr.get()); +} + +// ECMA 12.6.1 +JSValue* DoWhileNode::execute(ExecState* exec) +{ + JSValue* value = 0; + + while (1) { + exec->pushIteration(); + JSValue* statementValue = m_statement->execute(exec); + exec->popIteration(); + + if (exec->dynamicGlobalObject()->timedOut()) + return exec->setInterruptedCompletion(); + + if (statementValue) + value = statementValue; + + if (exec->completionType() != Normal) { + if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) + goto continueDoWhileLoop; + if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) + break; + return statementValue; + } + + continueDoWhileLoop: + bool b = m_expr->evaluateToBoolean(exec); + KJS_CHECKEXCEPTION + if (!b) + break; + } + + return exec->setNormalCompletion(value); +} + +// ------------------------------ WhileNode ------------------------------------ + +void WhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_statement.get()); + nodeStack.append(m_expr.get()); +} + +// ECMA 12.6.2 +JSValue* WhileNode::execute(ExecState* exec) +{ + JSValue* value = 0; + + while (1) { + bool b = m_expr->evaluateToBoolean(exec); + KJS_CHECKEXCEPTION + if (!b) + break; + + exec->pushIteration(); + JSValue* statementValue = m_statement->execute(exec); + exec->popIteration(); + + if (exec->dynamicGlobalObject()->timedOut()) + return exec->setInterruptedCompletion(); + + if (statementValue) + value = statementValue; + + if (exec->completionType() != Normal) { + if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) + continue; + if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) + break; + return statementValue; + } + } + + return exec->setNormalCompletion(value); +} + +// ------------------------------ ForNode -------------------------------------- + +void ForNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_statement.get()); + nodeStack.append(m_expr3.get()); + nodeStack.append(m_expr2.get()); + nodeStack.append(m_expr1.get()); +} + +// ECMA 12.6.3 +JSValue* ForNode::execute(ExecState* exec) +{ + JSValue* value = 0; + + m_expr1->evaluate(exec); + KJS_CHECKEXCEPTION + + while (1) { + bool b = m_expr2->evaluateToBoolean(exec); + KJS_CHECKEXCEPTION + if (!b) + break; + + exec->pushIteration(); + JSValue* statementValue = m_statement->execute(exec); + exec->popIteration(); + if (statementValue) + value = statementValue; + + if (exec->dynamicGlobalObject()->timedOut()) + return exec->setInterruptedCompletion(); + + if (exec->completionType() != Normal) { + if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) + goto continueForLoop; + if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) + break; + return statementValue; + } + + continueForLoop: + m_expr3->evaluate(exec); + KJS_CHECKEXCEPTION + } + + return exec->setNormalCompletion(value); +} + +// ------------------------------ ForInNode ------------------------------------ + +ForInNode::ForInNode(ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) + : m_init(0L) + , m_lexpr(l) + , m_expr(expr) + , m_statement(statement) + , m_identIsVarDecl(false) +{ +} + +ForInNode::ForInNode(const Identifier& ident, ExpressionNode* in, ExpressionNode* expr, StatementNode* statement) + : m_ident(ident) + , m_lexpr(new ResolveNode(ident)) + , m_expr(expr) + , m_statement(statement) + , m_identIsVarDecl(true) +{ + if (in) + m_init = new AssignResolveNode(ident, in); + // for( var foo = bar in baz ) +} + +void ForInNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_statement.get()); + nodeStack.append(m_expr.get()); + nodeStack.append(m_lexpr.get()); + if (m_init) + nodeStack.append(m_init.get()); +} + +// ECMA 12.6.4 +JSValue* ForInNode::execute(ExecState* exec) +{ + JSValue* value = 0; + + if (m_init) { + m_init->evaluate(exec); + KJS_CHECKEXCEPTION + } + + JSValue* e = m_expr->evaluate(exec); + KJS_CHECKEXCEPTION + + // For Null and Undefined, we want to make sure not to go through + // the loop at all, because toObject will throw an exception. + if (e->isUndefinedOrNull()) + return exec->setNormalCompletion(); + + JSObject* v = e->toObject(exec); + PropertyNameArray propertyNames; + v->getPropertyNames(exec, propertyNames); + + PropertyNameArray::const_iterator end = propertyNames.end(); + for (PropertyNameArray::const_iterator it = propertyNames.begin(); it != end; ++it) { + const Identifier& name = *it; + if (!v->hasProperty(exec, name)) + continue; + + JSValue* str = jsOwnedString(name.ustring()); + + if (m_lexpr->isResolveNode()) { + const Identifier& ident = static_cast<ResolveNode*>(m_lexpr.get())->identifier(); + + const ScopeChain& chain = exec->scopeChain(); + ScopeChainIterator iter = chain.begin(); + ScopeChainIterator end = chain.end(); + + // we must always have something in the scope chain + ASSERT(iter != end); + + PropertySlot slot; + JSObject* o; + do { + o = *iter; + if (o->getPropertySlot(exec, ident, slot)) { + o->put(exec, ident, str); + break; + } + ++iter; + } while (iter != end); + + if (iter == end) + o->put(exec, ident, str); + } else if (m_lexpr->isDotAccessorNode()) { + const Identifier& ident = static_cast<DotAccessorNode*>(m_lexpr.get())->identifier(); + JSValue* v = static_cast<DotAccessorNode*>(m_lexpr.get())->base()->evaluate(exec); + KJS_CHECKEXCEPTION + JSObject* o = v->toObject(exec); + + o->put(exec, ident, str); + } else { + ASSERT(m_lexpr->isBracketAccessorNode()); + JSValue* v = static_cast<BracketAccessorNode*>(m_lexpr.get())->base()->evaluate(exec); + KJS_CHECKEXCEPTION + JSValue* v2 = static_cast<BracketAccessorNode*>(m_lexpr.get())->subscript()->evaluate(exec); + KJS_CHECKEXCEPTION + JSObject* o = v->toObject(exec); + + uint32_t i; + if (v2->getUInt32(i)) + o->put(exec, i, str); + o->put(exec, Identifier(v2->toString(exec)), str); + } + + KJS_CHECKEXCEPTION + + exec->pushIteration(); + JSValue* statementValue = m_statement->execute(exec); + exec->popIteration(); + if (statementValue) + value = statementValue; + + if (exec->completionType() != Normal) { + if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) + continue; + if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) + break; + return statementValue; + } + } + + return exec->setNormalCompletion(value); +} + +// ------------------------------ ContinueNode --------------------------------- + +// ECMA 12.7 +JSValue* ContinueNode::execute(ExecState* exec) +{ + if (m_ident.isEmpty() && !exec->inIteration()) + return setErrorCompletion(exec, SyntaxError, "Invalid continue statement."); + if (!m_ident.isEmpty() && !exec->seenLabels().contains(m_ident)) + return setErrorCompletion(exec, SyntaxError, "Label %s not found.", m_ident); + return exec->setContinueCompletion(&m_ident); +} + +// ------------------------------ BreakNode ------------------------------------ + +// ECMA 12.8 +JSValue* BreakNode::execute(ExecState* exec) +{ + if (m_ident.isEmpty() && !exec->inIteration() && !exec->inSwitch()) + return setErrorCompletion(exec, SyntaxError, "Invalid break statement."); + if (!m_ident.isEmpty() && !exec->seenLabels().contains(m_ident)) + return setErrorCompletion(exec, SyntaxError, "Label %s not found."); + return exec->setBreakCompletion(&m_ident); +} + +// ------------------------------ ReturnNode ----------------------------------- + +void ReturnNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_value) + nodeStack.append(m_value.get()); +} + +// ECMA 12.9 +JSValue* ReturnNode::execute(ExecState* exec) +{ + CodeType codeType = exec->codeType(); + if (codeType != FunctionCode) + return setErrorCompletion(exec, SyntaxError, "Invalid return statement."); + + if (!m_value) + return exec->setReturnValueCompletion(jsUndefined()); + + JSValue* v = m_value->evaluate(exec); + KJS_CHECKEXCEPTION + + return exec->setReturnValueCompletion(v); +} + +// ------------------------------ WithNode ------------------------------------- + +void WithNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + // Can't optimize within statement because "with" introduces a dynamic scope. + nodeStack.append(m_expr.get()); +} + +// ECMA 12.10 +JSValue* WithNode::execute(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTION + JSObject* o = v->toObject(exec); + KJS_CHECKEXCEPTION + exec->dynamicGlobalObject()->tearOffActivation(exec); + exec->pushScope(o); + JSValue* value = m_statement->execute(exec); + exec->popScope(); + + return value; +} + +// ------------------------------ CaseClauseNode ------------------------------- + +void CaseClauseNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_expr) + nodeStack.append(m_expr.get()); + statementListPushFIFO(m_children, nodeStack); +} + +// ECMA 12.11 +JSValue* CaseClauseNode::evaluate(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTIONVALUE + + return v; +} + +// ECMA 12.11 +JSValue* CaseClauseNode::executeStatements(ExecState* exec) +{ + return statementListExecute(m_children, exec); +} + +// ------------------------------ ClauseListNode ------------------------------- + +void ClauseListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_next) + nodeStack.append(m_next.get()); + nodeStack.append(m_clause.get()); +} + +// ------------------------------ CaseBlockNode -------------------------------- + +CaseBlockNode::CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) + : m_list1(list1) + , m_defaultClause(defaultClause) + , m_list2(list2) +{ +} + +void CaseBlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + if (m_list2) + nodeStack.append(m_list2.get()); + if (m_defaultClause) + nodeStack.append(m_defaultClause.get()); + if (m_list1) + nodeStack.append(m_list1.get()); +} + +// ECMA 12.11 +JSValue* CaseBlockNode::executeBlock(ExecState* exec, JSValue* input) +{ + ClauseListNode* a = m_list1.get(); + while (a) { + CaseClauseNode* clause = a->getClause(); + a = a->getNext(); + JSValue* v = clause->evaluate(exec); + KJS_CHECKEXCEPTION + if (strictEqual(exec, input, v)) { + JSValue* res = clause->executeStatements(exec); + if (exec->completionType() != Normal) + return res; + for (; a; a = a->getNext()) { + JSValue* res = a->getClause()->executeStatements(exec); + if (exec->completionType() != Normal) + return res; + } + break; + } + } + + ClauseListNode* b = m_list2.get(); + while (b) { + CaseClauseNode* clause = b->getClause(); + b = b->getNext(); + JSValue* v = clause->evaluate(exec); + KJS_CHECKEXCEPTION + if (strictEqual(exec, input, v)) { + JSValue* res = clause->executeStatements(exec); + if (exec->completionType() != Normal) + return res; + goto step18; + } + } + + // default clause + if (m_defaultClause) { + JSValue* res = m_defaultClause->executeStatements(exec); + if (exec->completionType() != Normal) + return res; + } + b = m_list2.get(); +step18: + while (b) { + CaseClauseNode* clause = b->getClause(); + JSValue* res = clause->executeStatements(exec); + if (exec->completionType() != Normal) + return res; + b = b->getNext(); + } + + // bail out on error + KJS_CHECKEXCEPTION + + return exec->setNormalCompletion(); +} + +// ------------------------------ SwitchNode ----------------------------------- + +void SwitchNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_block.get()); + nodeStack.append(m_expr.get()); +} + +// ECMA 12.11 +JSValue* SwitchNode::execute(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTION + + exec->pushSwitch(); + JSValue* result = m_block->executeBlock(exec, v); + exec->popSwitch(); + + if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) + exec->setCompletionType(Normal); + return result; +} + +// ------------------------------ LabelNode ------------------------------------ + +void LabelNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_statement.get()); +} + +// ECMA 12.12 +JSValue* LabelNode::execute(ExecState* exec) +{ + if (!exec->seenLabels().push(m_label)) + return setErrorCompletion(exec, SyntaxError, "Duplicated label %s found.", m_label); + JSValue* result = m_statement->execute(exec); + exec->seenLabels().pop(); + + if (exec->completionType() == Break && exec->breakOrContinueTarget() == m_label) + exec->setCompletionType(Normal); + return result; +} + +// ------------------------------ ThrowNode ------------------------------------ + +void ThrowNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + nodeStack.append(m_expr.get()); +} + +// ECMA 12.13 +JSValue* ThrowNode::execute(ExecState* exec) +{ + JSValue* v = m_expr->evaluate(exec); + KJS_CHECKEXCEPTION + + handleException(exec, v); + return exec->setThrowCompletion(v); +} + +// ------------------------------ TryNode -------------------------------------- + +void TryNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +{ + // Can't optimize within catchBlock because "catch" introduces a dynamic scope. + if (m_finallyBlock) + nodeStack.append(m_finallyBlock.get()); + nodeStack.append(m_tryBlock.get()); +} + +// ECMA 12.14 +JSValue* TryNode::execute(ExecState* exec) +{ + JSValue* result = m_tryBlock->execute(exec); + + if (m_catchBlock && exec->completionType() == Throw) { + JSObject* obj = new JSObject; + obj->putDirect(m_exceptionIdent, result, DontDelete); + exec->dynamicGlobalObject()->tearOffActivation(exec); + exec->pushScope(obj); + result = m_catchBlock->execute(exec); + exec->popScope(); + } + + if (m_finallyBlock) { + ComplType savedCompletionType = exec->completionType(); + JSValue* finallyResult = m_finallyBlock->execute(exec); + if (exec->completionType() != Normal) + result = finallyResult; + else + exec->setCompletionType(savedCompletionType); + } + + return result; +} + +// ------------------------------ FunctionBodyNode ----------------------------- + +ScopeNode::ScopeNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) + : BlockNode(children) + , m_sourceURL(parser().sourceURL()) + , m_sourceId(parser().sourceId()) +{ + if (varStack) + m_varStack = *varStack; + if (funcStack) + m_functionStack = *funcStack; +} + +// ------------------------------ ProgramNode ----------------------------- + +ProgramNode::ProgramNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) + : ScopeNode(children, varStack, funcStack) +{ +} + +ProgramNode* ProgramNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) +{ + return new ProgramNode(children, varStack, funcStack); +} + +// ------------------------------ EvalNode ----------------------------- + +EvalNode::EvalNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) + : ScopeNode(children, varStack, funcStack) +{ +} + +EvalNode* EvalNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) +{ + return new EvalNode(children, varStack, funcStack); +} + +// ------------------------------ FunctionBodyNode ----------------------------- + +FunctionBodyNode::FunctionBodyNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) + : ScopeNode(children, varStack, funcStack) + , m_initialized(false) +{ +} + +FunctionBodyNode* FunctionBodyNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) +{ + if (Debugger::debuggersPresent) + return new FunctionBodyNodeWithDebuggerHooks(children, varStack, funcStack); + return new FunctionBodyNode(children, varStack, funcStack); +} + +void FunctionBodyNode::initializeSymbolTable(ExecState* exec) +{ + SymbolTable& symbolTable = exec->variableObject()->symbolTable(); + ASSERT(symbolTable.isEmpty()); + + size_t localStorageIndex = 0; + + // Order must match the order in processDeclarations. + + for (size_t i = 0, size = m_parameters.size(); i < size; ++i, ++localStorageIndex) { + UString::Rep* rep = m_parameters[i].ustring().rep(); + symbolTable.set(rep, localStorageIndex); + } + + for (size_t i = 0, size = m_functionStack.size(); i < size; ++i, ++localStorageIndex) { + UString::Rep* rep = m_functionStack[i]->m_ident.ustring().rep(); + symbolTable.set(rep, localStorageIndex); + } + + for (size_t i = 0, size = m_varStack.size(); i < size; ++i, ++localStorageIndex) { + Identifier& ident = m_varStack[i].first; + if (ident == exec->propertyNames().arguments) + continue; + symbolTable.add(ident.ustring().rep(), localStorageIndex); + } +} + +void ProgramNode::initializeSymbolTable(ExecState* exec) +{ + // If a previous script defined a symbol with the same name as one of our + // symbols, to avoid breaking previously optimized nodes, we need to reuse + // the symbol's existing storage index. So, we can't be as efficient as + // FunctionBodyNode::initializeSymbolTable, which knows that no bindings + // have yet been made. + + JSVariableObject* variableObject = exec->variableObject(); + SymbolTable& symbolTable = variableObject->symbolTable(); + + size_t localStorageIndex = symbolTable.size(); + size_t size; + + // Order must match the order in processDeclarations. + + size = m_functionStack.size(); + m_functionIndexes.resize(size); + for (size_t i = 0; i < size; ++i) { + UString::Rep* rep = m_functionStack[i]->m_ident.ustring().rep(); + pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex); + m_functionIndexes[i] = result.first->second; + if (result.second) + ++localStorageIndex; + } + + size = m_varStack.size(); + m_varIndexes.resize(size); + for (size_t i = 0; i < size; ++i) { + const Identifier& ident = m_varStack[i].first; + if (variableObject->hasProperty(exec, ident)) { + m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration. + continue; + } + + UString::Rep* rep = ident.ustring().rep(); + pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex); + if (!result.second) { + m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration. + continue; + } + + m_varIndexes[i] = result.first->second; + ++localStorageIndex; + } +} + +void ScopeNode::optimizeVariableAccess(ExecState* exec) +{ + NodeStack nodeStack; + Node* node = statementListInitializeVariableAccessStack(m_children, nodeStack); + if (!node) + return; + + const SymbolTable& symbolTable = exec->variableObject()->symbolTable(); + const LocalStorage& localStorage = exec->variableObject()->localStorage(); + while (true) { + node->optimizeVariableAccess(symbolTable, localStorage, nodeStack); + + size_t size = nodeStack.size(); + if (!size) + break; + --size; + node = nodeStack[size]; + nodeStack.shrink(size); + } +} + +void FunctionBodyNode::processDeclarations(ExecState* exec) +{ + if (!m_initialized) + initializeSymbolTable(exec); + + if (!m_functionStack.isEmpty()) + exec->dynamicGlobalObject()->tearOffActivation(exec); + + LocalStorage& localStorage = exec->variableObject()->localStorage(); + + // We can't just resize localStorage here because that would temporarily + // leave uninitialized entries, which would crash GC during the mark phase. + size_t totalSize = m_varStack.size() + m_parameters.size() + m_functionStack.size(); + if (totalSize > localStorage.capacity()) // Doing this check inline avoids function call overhead. + localStorage.reserveCapacity(totalSize); + + int minAttributes = DontDelete; + + // In order for our localStorage indexes to be correct, we must match the + // order of addition in initializeSymbolTable(). + + const List& args = *exec->arguments(); + for (size_t i = 0, size = m_parameters.size(); i < size; ++i) + localStorage.uncheckedAppend(LocalStorageEntry(args[i], DontDelete)); + + for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) { + FuncDeclNode* node = m_functionStack[i]; + localStorage.uncheckedAppend(LocalStorageEntry(node->makeFunction(exec), minAttributes)); + } + + for (size_t i = 0, size = m_varStack.size(); i < size; ++i) { + int attributes = minAttributes; + if (m_varStack[i].second & DeclarationStacks::IsConstant) + attributes |= ReadOnly; + localStorage.uncheckedAppend(LocalStorageEntry(jsUndefined(), attributes)); + } + + if (!m_initialized) { + optimizeVariableAccess(exec); + m_initialized = true; + } +} + +static void gccIsCrazy() KJS_FAST_CALL; +static void gccIsCrazy() +{ +} + +void ProgramNode::processDeclarations(ExecState* exec) +{ + // If you remove this call, some SunSpider tests, including + // bitops-nsieve-bits.js, will regress substantially on Mac, due to a ~40% + // increase in L2 cache misses. FIXME: <rdar://problem/5657439> WTF? + gccIsCrazy(); + + initializeSymbolTable(exec); + + LocalStorage& localStorage = exec->variableObject()->localStorage(); + + // We can't just resize localStorage here because that would temporarily + // leave uninitialized entries, which would crash GC during the mark phase. + localStorage.reserveCapacity(localStorage.size() + m_varStack.size() + m_functionStack.size()); + + int minAttributes = DontDelete; + + // In order for our localStorage indexes to be correct, we must match the + // order of addition in initializeSymbolTable(). + + for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) { + FuncDeclNode* node = m_functionStack[i]; + LocalStorageEntry entry = LocalStorageEntry(node->makeFunction(exec), minAttributes); + size_t index = m_functionIndexes[i]; + + if (index == localStorage.size()) + localStorage.uncheckedAppend(entry); + else { + ASSERT(index < localStorage.size()); + localStorage[index] = entry; + } + } + + for (size_t i = 0, size = m_varStack.size(); i < size; ++i) { + size_t index = m_varIndexes[i]; + if (index == missingSymbolMarker()) + continue; + + int attributes = minAttributes; + if (m_varStack[i].second & DeclarationStacks::IsConstant) + attributes |= ReadOnly; + LocalStorageEntry entry = LocalStorageEntry(jsUndefined(), attributes); + + ASSERT(index == localStorage.size()); + localStorage.uncheckedAppend(entry); + } + + optimizeVariableAccess(exec); +} + +void EvalNode::processDeclarations(ExecState* exec) +{ + // We could optimize access to pre-existing symbols here, but SunSpider + // reports that to be a net loss. + + size_t i; + size_t size; + + JSVariableObject* variableObject = exec->variableObject(); + + for (i = 0, size = m_varStack.size(); i < size; ++i) { + Identifier& ident = m_varStack[i].first; + if (variableObject->hasProperty(exec, ident)) + continue; + int attributes = 0; + if (m_varStack[i].second & DeclarationStacks::IsConstant) + attributes = ReadOnly; + variableObject->initializeVariable(exec, ident, jsUndefined(), attributes); + } + + for (i = 0, size = m_functionStack.size(); i < size; ++i) { + FuncDeclNode* funcDecl = m_functionStack[i]; + variableObject->initializeVariable(exec, funcDecl->m_ident, funcDecl->makeFunction(exec), 0); + } +} + +UString FunctionBodyNode::paramString() const +{ + UString s(""); + size_t count = m_parameters.size(); + for (size_t pos = 0; pos < count; ++pos) { + if (!s.isEmpty()) + s += ", "; + s += m_parameters[pos].ustring(); + } + + return s; +} + +JSValue* ProgramNode::execute(ExecState* exec) +{ + processDeclarations(exec); + return ScopeNode::execute(exec); +} + +JSValue* EvalNode::execute(ExecState* exec) +{ + processDeclarations(exec); + return ScopeNode::execute(exec); +} + +JSValue* FunctionBodyNode::execute(ExecState* exec) +{ + processDeclarations(exec); + return ScopeNode::execute(exec); +} + +// ------------------------------ FunctionBodyNodeWithDebuggerHooks --------------------------------- + +FunctionBodyNodeWithDebuggerHooks::FunctionBodyNodeWithDebuggerHooks(SourceElements* children, DeclarationStacks::VarStack* varStack, DeclarationStacks::FunctionStack* funcStack) + : FunctionBodyNode(children, varStack, funcStack) +{ +} + +JSValue* FunctionBodyNodeWithDebuggerHooks::execute(ExecState* exec) +{ + if (Debugger* dbg = exec->dynamicGlobalObject()->debugger()) { + if (!dbg->callEvent(exec, sourceId(), lineNo(), exec->function(), *exec->arguments())) + return exec->setInterruptedCompletion(); + } + + JSValue* result = FunctionBodyNode::execute(exec); + + if (Debugger* dbg = exec->dynamicGlobalObject()->debugger()) { + if (exec->completionType() == Throw) + exec->setException(result); + if (!dbg->returnEvent(exec, sourceId(), lineNo(), exec->function())) + return exec->setInterruptedCompletion(); + } + + return result; +} + +// ------------------------------ FuncDeclNode --------------------------------- + +void FuncDeclNode::addParams() +{ + for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam()) + m_body->parameters().append(p->ident()); +} + +FunctionImp* FuncDeclNode::makeFunction(ExecState* exec) +{ + FunctionImp* func = new FunctionImp(exec, m_ident, m_body.get(), exec->scopeChain()); + + JSObject* proto = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); + proto->putDirect(exec->propertyNames().constructor, func, DontEnum); + func->putDirect(exec->propertyNames().prototype, proto, DontDelete); + func->putDirect(exec->propertyNames().length, jsNumber(m_body->parameters().size()), ReadOnly | DontDelete | DontEnum); + return func; +} + +JSValue* FuncDeclNode::execute(ExecState* exec) +{ + return exec->setNormalCompletion(); +} + +// ------------------------------ FuncExprNode --------------------------------- + +// ECMA 13 +void FuncExprNode::addParams() +{ + for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam()) + m_body->parameters().append(p->ident()); +} + +JSValue* FuncExprNode::evaluate(ExecState* exec) +{ + exec->dynamicGlobalObject()->tearOffActivation(exec); + + bool named = !m_ident.isNull(); + JSObject* functionScopeObject = 0; + + if (named) { + // named FunctionExpressions can recursively call themselves, + // but they won't register with the current scope chain and should + // be contained as single property in an anonymous object. + functionScopeObject = new JSObject; + exec->pushScope(functionScopeObject); + } + + FunctionImp* func = new FunctionImp(exec, m_ident, m_body.get(), exec->scopeChain()); + JSObject* proto = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); + proto->putDirect(exec->propertyNames().constructor, func, DontEnum); + func->putDirect(exec->propertyNames().prototype, proto, DontDelete); + + if (named) { + functionScopeObject->putDirect(m_ident, func, ReadOnly | (exec->codeType() == EvalCode ? 0 : DontDelete)); + exec->popScope(); + } + + return func; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/nodes.h b/JavaScriptCore/kjs/nodes.h new file mode 100644 index 0000000..f366557 --- /dev/null +++ b/JavaScriptCore/kjs/nodes.h @@ -0,0 +1,2907 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * 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. + * + */ + +#ifndef NODES_H_ +#define NODES_H_ + +#include "internal.h" +#include "regexp.h" +#include "SymbolTable.h" +#include <wtf/ListRefPtr.h> +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +#if PLATFORM(X86) && COMPILER(GCC) +#define KJS_FAST_CALL __attribute__((regparm(3))) +#else +#define KJS_FAST_CALL +#endif + +namespace KJS { + + class ConstDeclNode; + class FuncDeclNode; + class Node; + class PropertyListNode; + class SourceStream; + + enum Operator { + OpEqual, + OpPlusEq, + OpMinusEq, + OpMultEq, + OpDivEq, + OpPlusPlus, + OpMinusMinus, + OpAndEq, + OpXOrEq, + OpOrEq, + OpModEq, + OpLShift, + OpRShift, + OpURShift, + }; + + enum Precedence { + PrecPrimary, + PrecMember, + PrecCall, + PrecLeftHandSide, + PrecPostfix, + PrecUnary, + PrecMultiplicitave, + PrecAdditive, + PrecShift, + PrecRelational, + PrecEquality, + PrecBitwiseAnd, + PrecBitwiseXor, + PrecBitwiseOr, + PrecLogicalAnd, + PrecLogicalOr, + PrecConditional, + PrecAssignment, + PrecExpression + }; + + struct DeclarationStacks { + typedef Vector<Node*, 16> NodeStack; + enum { IsConstant = 1, HasInitializer = 2 } VarAttrs; + typedef Vector<std::pair<Identifier, unsigned>, 16> VarStack; + typedef Vector<FuncDeclNode*, 16> FunctionStack; + + DeclarationStacks(ExecState* e, NodeStack& n, VarStack& v, FunctionStack& f) + : exec(e) + , nodeStack(n) + , varStack(v) + , functionStack(f) + { + } + + ExecState* exec; + NodeStack& nodeStack; + VarStack& varStack; + FunctionStack& functionStack; + }; + + class ParserRefCounted : Noncopyable { + protected: + ParserRefCounted() KJS_FAST_CALL; + ParserRefCounted(PlacementNewAdoptType) KJS_FAST_CALL + { + } + + public: + void ref() KJS_FAST_CALL; + void deref() KJS_FAST_CALL; + unsigned refcount() KJS_FAST_CALL; + + static void deleteNewObjects() KJS_FAST_CALL; + + virtual ~ParserRefCounted(); + }; + + class Node : public ParserRefCounted { + public: + typedef DeclarationStacks::NodeStack NodeStack; + typedef DeclarationStacks::VarStack VarStack; + typedef DeclarationStacks::FunctionStack FunctionStack; + + Node() KJS_FAST_CALL; + Node(PlacementNewAdoptType placementAdopt) KJS_FAST_CALL + : ParserRefCounted(placementAdopt) + { + } + + UString toString() const KJS_FAST_CALL; + int lineNo() const KJS_FAST_CALL { return m_line; } + + // Serialization. + virtual void streamTo(SourceStream&) const KJS_FAST_CALL = 0; + virtual Precedence precedence() const = 0; + virtual bool needsParensIfLeftmost() const { return false; } + + // Used for iterative, depth-first traversal of the node tree. Does not cross function call boundaries. + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL { } + + protected: + Node(JSType) KJS_FAST_CALL; // used by ExpressionNode + + // for use in execute() + JSValue* setErrorCompletion(ExecState*, ErrorType, const char* msg) KJS_FAST_CALL; + JSValue* setErrorCompletion(ExecState*, ErrorType, const char* msg, const Identifier&) KJS_FAST_CALL; + + // for use in evaluate() + JSValue* throwError(ExecState*, ErrorType, const char* msg) KJS_FAST_CALL; + JSValue* throwError(ExecState*, ErrorType, const char* msg, const char*) KJS_FAST_CALL; + JSValue* throwError(ExecState*, ErrorType, const char* msg, JSValue*, Node*) KJS_FAST_CALL; + JSValue* throwError(ExecState*, ErrorType, const char* msg, const Identifier&) KJS_FAST_CALL; + JSValue* throwError(ExecState*, ErrorType, const char* msg, JSValue*, const Identifier&) KJS_FAST_CALL; + JSValue* throwError(ExecState*, ErrorType, const char* msg, JSValue*, Node*, Node*) KJS_FAST_CALL; + JSValue* throwError(ExecState*, ErrorType, const char* msg, JSValue*, Node*, const Identifier&) KJS_FAST_CALL; + + JSValue* throwUndefinedVariableError(ExecState*, const Identifier&) KJS_FAST_CALL; + + void handleException(ExecState*) KJS_FAST_CALL; + void handleException(ExecState*, JSValue*) KJS_FAST_CALL; + + // for use in execute() + JSValue* rethrowException(ExecState*) KJS_FAST_CALL; + + int m_line : 28; + unsigned m_expectedReturnType : 3; // JSType + }; + + class ExpressionNode : public Node { + public: + ExpressionNode() KJS_FAST_CALL : Node() {} + ExpressionNode(JSType expectedReturn) KJS_FAST_CALL + : Node(expectedReturn) + { + } + + // Special constructor for cases where we overwrite an object in place. + ExpressionNode(PlacementNewAdoptType) KJS_FAST_CALL + : Node(PlacementNewAdopt) + { + } + + virtual bool isNumber() const KJS_FAST_CALL { return false; } + virtual bool isLocation() const KJS_FAST_CALL { return false; } + virtual bool isResolveNode() const KJS_FAST_CALL { return false; } + virtual bool isBracketAccessorNode() const KJS_FAST_CALL { return false; } + virtual bool isDotAccessorNode() const KJS_FAST_CALL { return false; } + + JSType expectedReturnType() const KJS_FAST_CALL { return static_cast<JSType>(m_expectedReturnType); } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL = 0; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + + // Used to optimize those nodes that do extra work when returning a result, even if the result has no semantic relevance + virtual void optimizeForUnnecessaryResult() { } + }; + + class StatementNode : public Node { + public: + StatementNode() KJS_FAST_CALL; + void setLoc(int line0, int line1) KJS_FAST_CALL; + int firstLine() const KJS_FAST_CALL { return lineNo(); } + int lastLine() const KJS_FAST_CALL { return m_lastLine; } + virtual JSValue* execute(ExecState *exec) KJS_FAST_CALL = 0; + virtual void pushLabel(const Identifier& ident) KJS_FAST_CALL { m_labelStack.push(ident); } + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + virtual bool isEmptyStatement() const KJS_FAST_CALL { return false; } + + protected: + LabelStack m_labelStack; + + private: + int m_lastLine; + }; + + class NullNode : public ExpressionNode { + public: + NullNode() KJS_FAST_CALL : ExpressionNode(NullType) {} + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + }; + + class FalseNode : public ExpressionNode { + public: + FalseNode() KJS_FAST_CALL + : ExpressionNode(BooleanType) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL { return false; } + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + }; + + class TrueNode : public ExpressionNode { + public: + TrueNode() KJS_FAST_CALL + : ExpressionNode(BooleanType) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL { return true; } + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + }; + + class PlaceholderTrueNode : public TrueNode { + public: + // Like TrueNode, but does not serialize as "true". + PlaceholderTrueNode() KJS_FAST_CALL + : TrueNode() + { + } + + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class NumberNode : public ExpressionNode { + public: + NumberNode(double v) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_double(v) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return signbit(m_double) ? PrecUnary : PrecPrimary; } + + virtual bool isNumber() const KJS_FAST_CALL { return true; } + double value() const KJS_FAST_CALL { return m_double; } + virtual void setValue(double d) KJS_FAST_CALL { m_double = d; } + + protected: + double m_double; + }; + + class ImmediateNumberNode : public NumberNode { + public: + ImmediateNumberNode(JSValue* v, double d) KJS_FAST_CALL + : NumberNode(d) + , m_value(v) + { + ASSERT(v == JSImmediate::from(d)); + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + + virtual void setValue(double d) KJS_FAST_CALL { m_double = d; m_value = JSImmediate::from(d); ASSERT(m_value); } + + private: + JSValue* m_value; // This is never a JSCell, only JSImmediate, thus no ProtectedPtr + }; + + class StringNode : public ExpressionNode { + public: + StringNode(const UString* v) KJS_FAST_CALL + : ExpressionNode(StringType) + , m_value(*v) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + + private: + UString m_value; + }; + + class RegExpNode : public ExpressionNode { + public: + RegExpNode(const UString& pattern, const UString& flags) KJS_FAST_CALL + : m_regExp(RegExp::create(pattern, flags)) + { + } + + JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + + private: + RefPtr<RegExp> m_regExp; + }; + + class ThisNode : public ExpressionNode { + public: + ThisNode() KJS_FAST_CALL + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + }; + + class ResolveNode : public ExpressionNode { + public: + ResolveNode(const Identifier& ident) KJS_FAST_CALL + : m_ident(ident) + { + } + + // Special constructor for cases where we overwrite an object in place. + ResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + + virtual bool isLocation() const KJS_FAST_CALL { return true; } + virtual bool isResolveNode() const KJS_FAST_CALL { return true; } + const Identifier& identifier() const KJS_FAST_CALL { return m_ident; } + + protected: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + Identifier m_ident; + size_t m_index; // Used by LocalVarAccessNode. + }; + + class LocalVarAccessNode : public ResolveNode { + public: + // Overwrites a ResolveNode in place. + LocalVarAccessNode(size_t i) KJS_FAST_CALL + : ResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + + private: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + }; + + class ElementNode : public Node { + public: + ElementNode(int elision, ExpressionNode* node) KJS_FAST_CALL + : m_elision(elision) + , m_node(node) + { + } + + ElementNode(ElementNode* l, int elision, ExpressionNode* node) KJS_FAST_CALL + : m_elision(elision) + , m_node(node) + { + l->m_next = this; + } + + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + + PassRefPtr<ElementNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + + JSValue* evaluate(ExecState*) KJS_FAST_CALL; + + private: + friend class ArrayNode; + ListRefPtr<ElementNode> m_next; + int m_elision; + RefPtr<ExpressionNode> m_node; + }; + + class ArrayNode : public ExpressionNode { + public: + ArrayNode(int elision) KJS_FAST_CALL + : m_elision(elision) + , m_optional(true) + { + } + + ArrayNode(ElementNode* element) KJS_FAST_CALL + : m_element(element) + , m_elision(0) + , m_optional(false) + { + } + + ArrayNode(int elision, ElementNode* element) KJS_FAST_CALL + : m_element(element) + , m_elision(elision) + , m_optional(true) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + + private: + RefPtr<ElementNode> m_element; + int m_elision; + bool m_optional; + }; + + class PropertyNode : public Node { + public: + enum Type { Constant, Getter, Setter }; + + PropertyNode(const Identifier& name, ExpressionNode* assign, Type type) KJS_FAST_CALL + : m_name(name) + , m_assign(assign) + , m_type(type) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + JSValue* evaluate(ExecState*) KJS_FAST_CALL; + const Identifier& name() const { return m_name; } + + private: + friend class PropertyListNode; + Identifier m_name; + RefPtr<ExpressionNode> m_assign; + Type m_type; + }; + + class PropertyListNode : public Node { + public: + PropertyListNode(PropertyNode* node) KJS_FAST_CALL + : m_node(node) + { + } + + PropertyListNode(PropertyNode* node, PropertyListNode* list) KJS_FAST_CALL + : m_node(node) + { + list->m_next = this; + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + JSValue* evaluate(ExecState*) KJS_FAST_CALL; + PassRefPtr<PropertyListNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + + private: + friend class ObjectLiteralNode; + RefPtr<PropertyNode> m_node; + ListRefPtr<PropertyListNode> m_next; + }; + + class ObjectLiteralNode : public ExpressionNode { + public: + ObjectLiteralNode() KJS_FAST_CALL + { + } + + ObjectLiteralNode(PropertyListNode* list) KJS_FAST_CALL + : m_list(list) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + virtual bool needsParensIfLeftmost() const { return true; } + + private: + RefPtr<PropertyListNode> m_list; + }; + + class BracketAccessorNode : public ExpressionNode { + public: + BracketAccessorNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecMember; } + + virtual bool isLocation() const KJS_FAST_CALL { return true; } + virtual bool isBracketAccessorNode() const KJS_FAST_CALL { return true; } + ExpressionNode* base() KJS_FAST_CALL { return m_base.get(); } + ExpressionNode* subscript() KJS_FAST_CALL { return m_subscript.get(); } + + private: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + }; + + class DotAccessorNode : public ExpressionNode { + public: + DotAccessorNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecMember; } + + virtual bool isLocation() const KJS_FAST_CALL { return true; } + virtual bool isDotAccessorNode() const KJS_FAST_CALL { return true; } + ExpressionNode* base() const KJS_FAST_CALL { return m_base.get(); } + const Identifier& identifier() const KJS_FAST_CALL { return m_ident; } + + private: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + }; + + class ArgumentListNode : public Node { + public: + ArgumentListNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + ArgumentListNode(ArgumentListNode* listNode, ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + listNode->m_next = this; + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + void evaluateList(ExecState*, List&) KJS_FAST_CALL; + PassRefPtr<ArgumentListNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + + private: + friend class ArgumentsNode; + ListRefPtr<ArgumentListNode> m_next; + RefPtr<ExpressionNode> m_expr; + }; + + class ArgumentsNode : public Node { + public: + ArgumentsNode() KJS_FAST_CALL + { + } + + ArgumentsNode(ArgumentListNode* listNode) KJS_FAST_CALL + : m_listNode(listNode) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + void evaluateList(ExecState* exec, List& list) KJS_FAST_CALL { if (m_listNode) m_listNode->evaluateList(exec, list); } + + private: + RefPtr<ArgumentListNode> m_listNode; + }; + + class NewExprNode : public ExpressionNode { + public: + NewExprNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + NewExprNode(ExpressionNode* expr, ArgumentsNode* args) KJS_FAST_CALL + : m_expr(expr) + , m_args(args) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecLeftHandSide; } + + private: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + + RefPtr<ExpressionNode> m_expr; + RefPtr<ArgumentsNode> m_args; + }; + + class FunctionCallValueNode : public ExpressionNode { + public: + FunctionCallValueNode(ExpressionNode* expr, ArgumentsNode* args) KJS_FAST_CALL + : m_expr(expr) + , m_args(args) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecCall; } + + private: + RefPtr<ExpressionNode> m_expr; + RefPtr<ArgumentsNode> m_args; + }; + + class FunctionCallResolveNode : public ExpressionNode { + public: + FunctionCallResolveNode(const Identifier& ident, ArgumentsNode* args) KJS_FAST_CALL + : m_ident(ident) + , m_args(args) + { + } + + FunctionCallResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + , m_args(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecCall; } + + protected: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + + Identifier m_ident; + RefPtr<ArgumentsNode> m_args; + size_t m_index; // Used by LocalVarFunctionCallNode. + }; + + class LocalVarFunctionCallNode : public FunctionCallResolveNode { + public: + LocalVarFunctionCallNode(size_t i) KJS_FAST_CALL + : FunctionCallResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + + private: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + }; + + class FunctionCallBracketNode : public ExpressionNode { + public: + FunctionCallBracketNode(ExpressionNode* base, ExpressionNode* subscript, ArgumentsNode* args) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + , m_args(args) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecCall; } + + protected: + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + RefPtr<ArgumentsNode> m_args; + }; + + class FunctionCallDotNode : public ExpressionNode { + public: + FunctionCallDotNode(ExpressionNode* base, const Identifier& ident, ArgumentsNode* args) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + , m_args(args) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecCall; } + + private: + ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + RefPtr<ArgumentsNode> m_args; + }; + + class PrePostResolveNode : public ExpressionNode { + public: + PrePostResolveNode(const Identifier& ident) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_ident(ident) + { + } + + PrePostResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + { + } + + protected: + Identifier m_ident; + size_t m_index; // Used by LocalVarPostfixNode. + }; + + class PostIncResolveNode : public PrePostResolveNode { + public: + PostIncResolveNode(const Identifier& ident) KJS_FAST_CALL + : PrePostResolveNode(ident) + { + } + + PostIncResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : PrePostResolveNode(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPostfix; } + virtual void optimizeForUnnecessaryResult(); + }; + + class PostIncLocalVarNode : public PostIncResolveNode { + public: + PostIncLocalVarNode(size_t i) KJS_FAST_CALL + : PostIncResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void optimizeForUnnecessaryResult(); + }; + + class PostIncConstNode : public PostIncResolveNode { + public: + PostIncConstNode(size_t i) KJS_FAST_CALL + : PostIncResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class PostDecResolveNode : public PrePostResolveNode { + public: + PostDecResolveNode(const Identifier& ident) KJS_FAST_CALL + : PrePostResolveNode(ident) + { + } + + PostDecResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : PrePostResolveNode(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPostfix; } + virtual void optimizeForUnnecessaryResult(); + }; + + class PostDecLocalVarNode : public PostDecResolveNode { + public: + PostDecLocalVarNode(size_t i) KJS_FAST_CALL + : PostDecResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void optimizeForUnnecessaryResult(); + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + }; + + class PostDecConstNode : public PostDecResolveNode { + public: + PostDecConstNode(size_t i) KJS_FAST_CALL + : PostDecResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class PostfixBracketNode : public ExpressionNode { + public: + PostfixBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPostfix; } + + protected: + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + }; + + class PostIncBracketNode : public PostfixBracketNode { + public: + PostIncBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : PostfixBracketNode(base, subscript) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PostDecBracketNode : public PostfixBracketNode { + public: + PostDecBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : PostfixBracketNode(base, subscript) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PostfixDotNode : public ExpressionNode { + public: + PostfixDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPostfix; } + + protected: + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + }; + + class PostIncDotNode : public PostfixDotNode { + public: + PostIncDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : PostfixDotNode(base, ident) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PostDecDotNode : public PostfixDotNode { + public: + PostDecDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : PostfixDotNode(base, ident) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PostfixErrorNode : public ExpressionNode { + public: + PostfixErrorNode(ExpressionNode* expr, Operator oper) KJS_FAST_CALL + : m_expr(expr) + , m_operator(oper) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPostfix; } + + private: + RefPtr<ExpressionNode> m_expr; + Operator m_operator; + }; + + class DeleteResolveNode : public ExpressionNode { + public: + DeleteResolveNode(const Identifier& ident) KJS_FAST_CALL + : m_ident(ident) + { + } + + DeleteResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + Identifier m_ident; + }; + + class LocalVarDeleteNode : public DeleteResolveNode { + public: + LocalVarDeleteNode() KJS_FAST_CALL + : DeleteResolveNode(PlacementNewAdopt) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class DeleteBracketNode : public ExpressionNode { + public: + DeleteBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + }; + + class DeleteDotNode : public ExpressionNode { + public: + DeleteDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + }; + + class DeleteValueNode : public ExpressionNode { + public: + DeleteValueNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class VoidNode : public ExpressionNode { + public: + VoidNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class TypeOfResolveNode : public ExpressionNode { + public: + TypeOfResolveNode(const Identifier& ident) KJS_FAST_CALL + : ExpressionNode(StringType) + , m_ident(ident) + { + } + + TypeOfResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + { + m_expectedReturnType = StringType; + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + const Identifier& identifier() const KJS_FAST_CALL { return m_ident; } + + protected: + Identifier m_ident; + size_t m_index; // Used by LocalTypeOfNode. + }; + + class LocalVarTypeOfNode : public TypeOfResolveNode { + public: + LocalVarTypeOfNode(size_t i) KJS_FAST_CALL + : TypeOfResolveNode(PlacementNewAdopt) + { + m_expectedReturnType = StringType; + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class TypeOfValueNode : public ExpressionNode { + public: + TypeOfValueNode(ExpressionNode* expr) KJS_FAST_CALL + : ExpressionNode(StringType) + , m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class PreIncResolveNode : public PrePostResolveNode { + public: + PreIncResolveNode(const Identifier& ident) KJS_FAST_CALL + : PrePostResolveNode(ident) + { + } + + PreIncResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : PrePostResolveNode(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + }; + + class PreIncLocalVarNode : public PreIncResolveNode { + public: + PreIncLocalVarNode(size_t i) KJS_FAST_CALL + : PreIncResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class PreIncConstNode : public PreIncResolveNode { + public: + PreIncConstNode(size_t i) KJS_FAST_CALL + : PreIncResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class PreDecResolveNode : public PrePostResolveNode { + public: + PreDecResolveNode(const Identifier& ident) KJS_FAST_CALL + : PrePostResolveNode(ident) + { + } + + PreDecResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : PrePostResolveNode(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + }; + + class PreDecLocalVarNode : public PreDecResolveNode { + public: + PreDecLocalVarNode(size_t i) KJS_FAST_CALL + : PreDecResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class PreDecConstNode : public PreDecResolveNode { + public: + PreDecConstNode(size_t i) KJS_FAST_CALL + : PreDecResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class PrefixBracketNode : public ExpressionNode { + public: + PrefixBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + protected: + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + }; + + class PreIncBracketNode : public PrefixBracketNode { + public: + PreIncBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : PrefixBracketNode(base, subscript) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PreDecBracketNode : public PrefixBracketNode { + public: + PreDecBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL + : PrefixBracketNode(base, subscript) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PrefixDotNode : public ExpressionNode { + public: + PrefixDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecPostfix; } + + protected: + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + }; + + class PreIncDotNode : public PrefixDotNode { + public: + PreIncDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : PrefixDotNode(base, ident) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PreDecDotNode : public PrefixDotNode { + public: + PreDecDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL + : PrefixDotNode(base, ident) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + }; + + class PrefixErrorNode : public ExpressionNode { + public: + PrefixErrorNode(ExpressionNode* expr, Operator oper) KJS_FAST_CALL + : m_expr(expr) + , m_operator(oper) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + Operator m_operator; + }; + + class UnaryPlusNode : public ExpressionNode { + public: + UnaryPlusNode(ExpressionNode* expr) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class NegateNode : public ExpressionNode { + public: + NegateNode(ExpressionNode* expr) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class BitwiseNotNode : public ExpressionNode { + public: + BitwiseNotNode(ExpressionNode* expr) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + ALWAYS_INLINE int32_t inlineEvaluateToInt32(ExecState*); + + RefPtr<ExpressionNode> m_expr; + }; + + class LogicalNotNode : public ExpressionNode { + public: + LogicalNotNode(ExpressionNode* expr) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class MultNode : public ExpressionNode { + public: + MultNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecMultiplicitave; } + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class DivNode : public ExpressionNode { + public: + DivNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecMultiplicitave; } + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class ModNode : public ExpressionNode { + public: + ModNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecMultiplicitave; } + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class AddNode : public ExpressionNode { + public: + AddNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAdditive; } + + protected: + AddNode(ExpressionNode* term1, ExpressionNode* term2, JSType expectedReturn) KJS_FAST_CALL + : ExpressionNode(expectedReturn) + , m_term1(term1) + , m_term2(term2) + { + } + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + }; + + class AddNumbersNode : public AddNode { + public: + AddNumbersNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : AddNode(term1, term2, NumberType) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*) KJS_FAST_CALL; + }; + + class AddStringLeftNode : public AddNode { + public: + AddStringLeftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : AddNode(term1, term2, StringType) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class AddStringRightNode : public AddNode { + public: + AddStringRightNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : AddNode(term1, term2, StringType) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class AddStringsNode : public AddNode { + public: + AddStringsNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : AddNode(term1, term2, StringType) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class SubNode : public ExpressionNode { + public: + SubNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAdditive; } + + private: + ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class LeftShiftNode : public ExpressionNode { + public: + LeftShiftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecShift; } + + private: + ALWAYS_INLINE int32_t inlineEvaluateToInt32(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class RightShiftNode : public ExpressionNode { + public: + RightShiftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecShift; } + + private: + ALWAYS_INLINE int32_t inlineEvaluateToInt32(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class UnsignedRightShiftNode : public ExpressionNode { + public: + UnsignedRightShiftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_term1(term1) + , m_term2(term2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecShift; } + private: + ALWAYS_INLINE uint32_t inlineEvaluateToUInt32(ExecState*); + + RefPtr<ExpressionNode> m_term1; + RefPtr<ExpressionNode> m_term2; + }; + + class LessNode : public ExpressionNode { + public: + LessNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + protected: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class LessNumbersNode : public LessNode { + public: + LessNumbersNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : LessNode(expr1, expr2) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + }; + + class LessStringsNode : public LessNode { + public: + LessStringsNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : LessNode(expr1, expr2) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + }; + + class GreaterNode : public ExpressionNode { + public: + GreaterNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class LessEqNode : public ExpressionNode { + public: + LessEqNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class GreaterEqNode : public ExpressionNode { + public: + GreaterEqNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class InstanceOfNode : public ExpressionNode { + public: + InstanceOfNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } + + private: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class InNode : public ExpressionNode { + public: + InNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } + + private: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class EqualNode : public ExpressionNode { + public: + EqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecEquality; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class NotEqualNode : public ExpressionNode { + public: + NotEqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecEquality; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class StrictEqualNode : public ExpressionNode { + public: + StrictEqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecEquality; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class NotStrictEqualNode : public ExpressionNode { + public: + NotStrictEqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecEquality; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class BitAndNode : public ExpressionNode { + public: + BitAndNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecBitwiseAnd; } + + private: + ALWAYS_INLINE int32_t inlineEvaluateToInt32(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class BitOrNode : public ExpressionNode { + public: + BitOrNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecBitwiseOr; } + + private: + ALWAYS_INLINE int32_t inlineEvaluateToInt32(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class BitXOrNode : public ExpressionNode { + public: + BitXOrNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(NumberType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecBitwiseXor; } + + private: + ALWAYS_INLINE int32_t inlineEvaluateToInt32(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + /** + * m_expr1 && m_expr2, m_expr1 || m_expr2 + */ + class LogicalAndNode : public ExpressionNode { + public: + LogicalAndNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecLogicalAnd; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class LogicalOrNode : public ExpressionNode { + public: + LogicalOrNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : ExpressionNode(BooleanType) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecLogicalOr; } + + private: + ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + /** + * The ternary operator, "m_logical ? m_expr1 : m_expr2" + */ + class ConditionalNode : public ExpressionNode { + public: + ConditionalNode(ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : m_logical(logical) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; + virtual double evaluateToNumber(ExecState*) KJS_FAST_CALL; + virtual int32_t evaluateToInt32(ExecState*) KJS_FAST_CALL; + virtual uint32_t evaluateToUInt32(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecConditional; } + + private: + RefPtr<ExpressionNode> m_logical; + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class ReadModifyResolveNode : public ExpressionNode { + public: + ReadModifyResolveNode(const Identifier& ident, Operator oper, ExpressionNode* right) KJS_FAST_CALL + : m_ident(ident) + , m_operator(oper) + , m_right(right) + { + } + + ReadModifyResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + , m_right(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + Identifier m_ident; + Operator m_operator; + RefPtr<ExpressionNode> m_right; + size_t m_index; // Used by ReadModifyLocalVarNode. + }; + + class ReadModifyLocalVarNode : public ReadModifyResolveNode { + public: + ReadModifyLocalVarNode(size_t i) KJS_FAST_CALL + : ReadModifyResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class ReadModifyConstNode : public ReadModifyResolveNode { + public: + ReadModifyConstNode(size_t i) KJS_FAST_CALL + : ReadModifyResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class AssignResolveNode : public ExpressionNode { + public: + AssignResolveNode(const Identifier& ident, ExpressionNode* right) KJS_FAST_CALL + : m_ident(ident) + , m_right(right) + { + } + + AssignResolveNode(PlacementNewAdoptType) KJS_FAST_CALL + : ExpressionNode(PlacementNewAdopt) + , m_ident(PlacementNewAdopt) + , m_right(PlacementNewAdopt) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + Identifier m_ident; + RefPtr<ExpressionNode> m_right; + size_t m_index; // Used by ReadModifyLocalVarNode. + }; + + class AssignLocalVarNode : public AssignResolveNode { + public: + AssignLocalVarNode(size_t i) KJS_FAST_CALL + : AssignResolveNode(PlacementNewAdopt) + { + ASSERT(i != missingSymbolMarker()); + m_index = i; + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class AssignConstNode : public AssignResolveNode { + public: + AssignConstNode() KJS_FAST_CALL + : AssignResolveNode(PlacementNewAdopt) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + }; + + class ReadModifyBracketNode : public ExpressionNode { + public: + ReadModifyBracketNode(ExpressionNode* base, ExpressionNode* subscript, Operator oper, ExpressionNode* right) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + , m_operator(oper) + , m_right(right) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + Operator m_operator; + RefPtr<ExpressionNode> m_right; + }; + + class AssignBracketNode : public ExpressionNode { + public: + AssignBracketNode(ExpressionNode* base, ExpressionNode* subscript, ExpressionNode* right) KJS_FAST_CALL + : m_base(base) + , m_subscript(subscript) + , m_right(right) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + RefPtr<ExpressionNode> m_base; + RefPtr<ExpressionNode> m_subscript; + RefPtr<ExpressionNode> m_right; + }; + + class AssignDotNode : public ExpressionNode { + public: + AssignDotNode(ExpressionNode* base, const Identifier& ident, ExpressionNode* right) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + , m_right(right) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + RefPtr<ExpressionNode> m_right; + }; + + class ReadModifyDotNode : public ExpressionNode { + public: + ReadModifyDotNode(ExpressionNode* base, const Identifier& ident, Operator oper, ExpressionNode* right) KJS_FAST_CALL + : m_base(base) + , m_ident(ident) + , m_operator(oper) + , m_right(right) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + RefPtr<ExpressionNode> m_base; + Identifier m_ident; + Operator m_operator; + RefPtr<ExpressionNode> m_right; + }; + + class AssignErrorNode : public ExpressionNode { + public: + AssignErrorNode(ExpressionNode* left, Operator oper, ExpressionNode* right) KJS_FAST_CALL + : m_left(left) + , m_operator(oper) + , m_right(right) + { + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecAssignment; } + + protected: + RefPtr<ExpressionNode> m_left; + Operator m_operator; + RefPtr<ExpressionNode> m_right; + }; + + class CommaNode : public ExpressionNode { + public: + CommaNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : m_expr1(expr1) + , m_expr2(expr2) + { + m_expr1->optimizeForUnnecessaryResult(); + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecExpression; } + + private: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + }; + + class VarDeclCommaNode : public CommaNode { + public: + VarDeclCommaNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL + : CommaNode(expr1, expr2) + { + } + virtual Precedence precedence() const { return PrecAssignment; } + }; + + class ConstDeclNode : public ExpressionNode { + public: + ConstDeclNode(const Identifier& ident, ExpressionNode* in) KJS_FAST_CALL; + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual KJS::JSValue* evaluate(ExecState*) KJS_FAST_CALL; + void evaluateSingle(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + PassRefPtr<ConstDeclNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + + Identifier m_ident; + ListRefPtr<ConstDeclNode> m_next; + RefPtr<ExpressionNode> m_init; + + private: + void handleSlowCase(ExecState*, const ScopeChain&, JSValue*) KJS_FAST_CALL NEVER_INLINE; + }; + + class ConstStatementNode : public StatementNode { + public: + ConstStatementNode(ConstDeclNode* next) KJS_FAST_CALL + : m_next(next) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ConstDeclNode> m_next; + }; + + typedef Vector<RefPtr<StatementNode> > StatementVector; + + class SourceElements : public ParserRefCounted { + public: + void append(PassRefPtr<StatementNode>); + void releaseContentsIntoVector(StatementVector& destination) + { + ASSERT(destination.isEmpty()); + m_statements.swap(destination); + } + + private: + StatementVector m_statements; + }; + + class BlockNode : public StatementNode { + public: + BlockNode(SourceElements* children) KJS_FAST_CALL; + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + protected: + StatementVector m_children; + }; + + class EmptyStatementNode : public StatementNode { + public: + EmptyStatementNode() KJS_FAST_CALL // debug + { + } + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual bool isEmptyStatement() const KJS_FAST_CALL { return true; } + }; + + class ExprStatementNode : public StatementNode { + public: + ExprStatementNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class VarStatementNode : public StatementNode { + public: + VarStatementNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class IfNode : public StatementNode { + public: + IfNode(ExpressionNode* condition, StatementNode* ifBlock) KJS_FAST_CALL + : m_condition(condition) + , m_ifBlock(ifBlock) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + protected: + RefPtr<ExpressionNode> m_condition; + RefPtr<StatementNode> m_ifBlock; + }; + + class IfElseNode : public IfNode { + public: + IfElseNode(ExpressionNode* condtion, StatementNode* ifBlock, StatementNode* elseBlock) KJS_FAST_CALL + : IfNode(condtion, ifBlock) + , m_elseBlock(elseBlock) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<StatementNode> m_elseBlock; + }; + + class DoWhileNode : public StatementNode { + public: + DoWhileNode(StatementNode* statement, ExpressionNode* expr) KJS_FAST_CALL + : m_statement(statement) + , m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<StatementNode> m_statement; + RefPtr<ExpressionNode> m_expr; + }; + + class WhileNode : public StatementNode { + public: + WhileNode(ExpressionNode* expr, StatementNode* statement) KJS_FAST_CALL + : m_expr(expr) + , m_statement(statement) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + RefPtr<StatementNode> m_statement; + }; + + class ForNode : public StatementNode { + public: + ForNode(ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode* statement, bool expr1WasVarDecl) KJS_FAST_CALL + : m_expr1(expr1 ? expr1 : new PlaceholderTrueNode) + , m_expr2(expr2 ? expr2 : new PlaceholderTrueNode) + , m_expr3(expr3 ? expr3 : new PlaceholderTrueNode) + , m_statement(statement) + , m_expr1WasVarDecl(expr1 && expr1WasVarDecl) + { + ASSERT(m_expr1); + ASSERT(m_expr2); + ASSERT(m_expr3); + ASSERT(statement); + + m_expr1->optimizeForUnnecessaryResult(); + m_expr3->optimizeForUnnecessaryResult(); + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + RefPtr<ExpressionNode> m_expr3; + RefPtr<StatementNode> m_statement; + bool m_expr1WasVarDecl; + }; + + class ForInNode : public StatementNode { + public: + ForInNode(ExpressionNode*, ExpressionNode*, StatementNode*) KJS_FAST_CALL; + ForInNode(const Identifier&, ExpressionNode*, ExpressionNode*, StatementNode*) KJS_FAST_CALL; + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + Identifier m_ident; + RefPtr<ExpressionNode> m_init; + RefPtr<ExpressionNode> m_lexpr; + RefPtr<ExpressionNode> m_expr; + RefPtr<StatementNode> m_statement; + bool m_identIsVarDecl; + }; + + class ContinueNode : public StatementNode { + public: + ContinueNode() KJS_FAST_CALL + { + } + + ContinueNode(const Identifier& ident) KJS_FAST_CALL + : m_ident(ident) + { + } + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + Identifier m_ident; + }; + + class BreakNode : public StatementNode { + public: + BreakNode() KJS_FAST_CALL + { + } + + BreakNode(const Identifier& ident) KJS_FAST_CALL + : m_ident(ident) + { + } + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + Identifier m_ident; + }; + + class ReturnNode : public StatementNode { + public: + ReturnNode(ExpressionNode* value) KJS_FAST_CALL + : m_value(value) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_value; + }; + + class WithNode : public StatementNode { + public: + WithNode(ExpressionNode* expr, StatementNode* statement) KJS_FAST_CALL + : m_expr(expr) + , m_statement(statement) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + RefPtr<StatementNode> m_statement; + }; + + class LabelNode : public StatementNode { + public: + LabelNode(const Identifier& label, StatementNode* statement) KJS_FAST_CALL + : m_label(label) + , m_statement(statement) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual void pushLabel(const Identifier& ident) KJS_FAST_CALL { m_statement->pushLabel(ident); } + + private: + Identifier m_label; + RefPtr<StatementNode> m_statement; + }; + + class ThrowNode : public StatementNode { + public: + ThrowNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + }; + + class TryNode : public StatementNode { + public: + TryNode(StatementNode* tryBlock, const Identifier& exceptionIdent, StatementNode* catchBlock, StatementNode* finallyBlock) KJS_FAST_CALL + : m_tryBlock(tryBlock) + , m_exceptionIdent(exceptionIdent) + , m_catchBlock(catchBlock) + , m_finallyBlock(finallyBlock) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<StatementNode> m_tryBlock; + Identifier m_exceptionIdent; + RefPtr<StatementNode> m_catchBlock; + RefPtr<StatementNode> m_finallyBlock; + }; + + class ParameterNode : public Node { + public: + ParameterNode(const Identifier& ident) KJS_FAST_CALL + : m_ident(ident) + { + } + + ParameterNode(ParameterNode* l, const Identifier& ident) KJS_FAST_CALL + : m_ident(ident) + { + l->m_next = this; + } + + Identifier ident() KJS_FAST_CALL { return m_ident; } + ParameterNode *nextParam() KJS_FAST_CALL { return m_next.get(); } + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + PassRefPtr<ParameterNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + private: + friend class FuncDeclNode; + friend class FuncExprNode; + Identifier m_ident; + ListRefPtr<ParameterNode> m_next; + }; + + class ScopeNode : public BlockNode { + public: + ScopeNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + int sourceId() const KJS_FAST_CALL { return m_sourceId; } + const UString& sourceURL() const KJS_FAST_CALL { return m_sourceURL; } + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + protected: + void optimizeVariableAccess(ExecState*) KJS_FAST_CALL; + + VarStack m_varStack; + FunctionStack m_functionStack; + + private: + UString m_sourceURL; + int m_sourceId; + }; + + class ProgramNode : public ScopeNode { + public: + static ProgramNode* create(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + + private: + ProgramNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + void initializeSymbolTable(ExecState*) KJS_FAST_CALL; + ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL; + + Vector<size_t> m_varIndexes; // Storage indexes belonging to the nodes in m_varStack. (Recorded to avoid double lookup.) + Vector<size_t> m_functionIndexes; // Storage indexes belonging to the nodes in m_functionStack. (Recorded to avoid double lookup.) + }; + + class EvalNode : public ScopeNode { + public: + static EvalNode* create(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + + private: + EvalNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL; + }; + + class FunctionBodyNode : public ScopeNode { + public: + static FunctionBodyNode* create(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + + SymbolTable& symbolTable() KJS_FAST_CALL { return m_symbolTable; } + + Vector<Identifier>& parameters() KJS_FAST_CALL { return m_parameters; } + UString paramString() const KJS_FAST_CALL; + + protected: + FunctionBodyNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + + private: + void initializeSymbolTable(ExecState*) KJS_FAST_CALL; + ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL; + + bool m_initialized; + Vector<Identifier> m_parameters; + SymbolTable m_symbolTable; + }; + + class FuncExprNode : public ExpressionNode { + public: + FuncExprNode(const Identifier& ident, FunctionBodyNode* body, ParameterNode* parameter = 0) KJS_FAST_CALL + : m_ident(ident) + , m_parameter(parameter) + , m_body(body) + { + addParams(); + } + + virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { return PrecMember; } + virtual bool needsParensIfLeftmost() const { return true; } + + private: + void addParams() KJS_FAST_CALL; + + // Used for streamTo + friend class PropertyNode; + Identifier m_ident; + RefPtr<ParameterNode> m_parameter; + RefPtr<FunctionBodyNode> m_body; + }; + + class FuncDeclNode : public StatementNode { + public: + FuncDeclNode(const Identifier& ident, FunctionBodyNode* body) KJS_FAST_CALL + : m_ident(ident) + , m_body(body) + { + addParams(); + } + + FuncDeclNode(const Identifier& ident, ParameterNode* parameter, FunctionBodyNode* body) KJS_FAST_CALL + : m_ident(ident) + , m_parameter(parameter) + , m_body(body) + { + addParams(); + } + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + ALWAYS_INLINE FunctionImp* makeFunction(ExecState*) KJS_FAST_CALL; + + Identifier m_ident; + + private: + void addParams() KJS_FAST_CALL; + + RefPtr<ParameterNode> m_parameter; + RefPtr<FunctionBodyNode> m_body; + }; + + class CaseClauseNode : public Node { + public: + CaseClauseNode(ExpressionNode* expr) KJS_FAST_CALL + : m_expr(expr) + { + } + + CaseClauseNode(ExpressionNode* expr, SourceElements* children) KJS_FAST_CALL + : m_expr(expr) + { + if (children) + children->releaseContentsIntoVector(m_children); + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + JSValue* evaluate(ExecState*) KJS_FAST_CALL; + JSValue* executeStatements(ExecState*) KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + StatementVector m_children; + }; + + class ClauseListNode : public Node { + public: + ClauseListNode(CaseClauseNode* clause) KJS_FAST_CALL + : m_clause(clause) + { + } + + ClauseListNode(ClauseListNode* clauseList, CaseClauseNode* clause) KJS_FAST_CALL + : m_clause(clause) + { + clauseList->m_next = this; + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + CaseClauseNode* getClause() const KJS_FAST_CALL { return m_clause.get(); } + ClauseListNode* getNext() const KJS_FAST_CALL { return m_next.get(); } + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + PassRefPtr<ClauseListNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + private: + friend class CaseBlockNode; + RefPtr<CaseClauseNode> m_clause; + ListRefPtr<ClauseListNode> m_next; + }; + + class CaseBlockNode : public Node { + public: + CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) KJS_FAST_CALL; + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + JSValue* executeBlock(ExecState*, JSValue *input) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } + + private: + RefPtr<ClauseListNode> m_list1; + RefPtr<CaseClauseNode> m_defaultClause; + RefPtr<ClauseListNode> m_list2; + }; + + class SwitchNode : public StatementNode { + public: + SwitchNode(ExpressionNode* expr, CaseBlockNode* block) KJS_FAST_CALL + : m_expr(expr) + , m_block(block) + { + } + + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + private: + RefPtr<ExpressionNode> m_expr; + RefPtr<CaseBlockNode> m_block; + }; + + class BreakpointCheckStatement : public StatementNode { + public: + BreakpointCheckStatement(PassRefPtr<StatementNode>) KJS_FAST_CALL; + + virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + + private: + RefPtr<StatementNode> m_statement; + }; + + struct ElementList { + ElementNode* head; + ElementNode* tail; + }; + + struct PropertyList { + PropertyListNode* head; + PropertyListNode* tail; + }; + + struct ArgumentList { + ArgumentListNode* head; + ArgumentListNode* tail; + }; + + struct ConstDeclList { + ConstDeclNode* head; + ConstDeclNode* tail; + }; + + struct ParameterList { + ParameterNode* head; + ParameterNode* tail; + }; + + struct ClauseList { + ClauseListNode* head; + ClauseListNode* tail; + }; + +} // namespace KJS + +#endif // NODES_H_ diff --git a/JavaScriptCore/kjs/nodes2string.cpp b/JavaScriptCore/kjs/nodes2string.cpp new file mode 100644 index 0000000..b10e180 --- /dev/null +++ b/JavaScriptCore/kjs/nodes2string.cpp @@ -0,0 +1,973 @@ +/* + * Copyright (C) 2002 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 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 "nodes.h" + +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/unicode/Unicode.h> + +using namespace WTF; +using namespace Unicode; + +namespace KJS { + +// A simple text streaming class that helps with code indentation. + +enum EndlType { Endl }; +enum IndentType { Indent }; +enum UnindentType { Unindent }; +enum DotExprType { DotExpr }; + +class SourceStream { +public: + SourceStream() + : m_numberNeedsParens(false) + , m_atStartOfStatement(true) + , m_precedence(PrecExpression) + { + } + + UString toString() const { return m_string; } + + SourceStream& operator<<(const Identifier&); + SourceStream& operator<<(const UString&); + SourceStream& operator<<(const char*); + SourceStream& operator<<(double); + SourceStream& operator<<(char); + SourceStream& operator<<(EndlType); + SourceStream& operator<<(IndentType); + SourceStream& operator<<(UnindentType); + SourceStream& operator<<(DotExprType); + SourceStream& operator<<(Precedence); + SourceStream& operator<<(const Node*); + template <typename T> SourceStream& operator<<(const RefPtr<T>& n) { return *this << n.get(); } + +private: + UString m_string; + UString m_spacesForIndentation; + bool m_numberNeedsParens; + bool m_atStartOfStatement; + Precedence m_precedence; +}; + +// -------- + +static UString escapeStringForPrettyPrinting(const UString& s) +{ + UString escapedString; + + for (int i = 0; i < s.size(); i++) { + unsigned short c = s.data()[i].unicode(); + switch (c) { + case '\"': + escapedString += "\\\""; + break; + case '\n': + escapedString += "\\n"; + break; + case '\r': + escapedString += "\\r"; + break; + case '\t': + escapedString += "\\t"; + break; + case '\\': + escapedString += "\\\\"; + break; + default: + if (c < 128 && isPrintableChar(c)) + escapedString.append(c); + else { + char hexValue[7]; + snprintf(hexValue, 7, "\\u%04x", c); + escapedString += hexValue; + } + } + } + + return escapedString; +} + +static const char* operatorString(Operator oper) +{ + switch (oper) { + case OpEqual: + return "="; + case OpMultEq: + return "*="; + case OpDivEq: + return "/="; + case OpPlusEq: + return "+="; + case OpMinusEq: + return "-="; + case OpLShift: + return "<<="; + case OpRShift: + return ">>="; + case OpURShift: + return ">>>="; + case OpAndEq: + return "&="; + case OpXOrEq: + return "^="; + case OpOrEq: + return "|="; + case OpModEq: + return "%="; + case OpPlusPlus: + return "++"; + case OpMinusMinus: + return "--"; + } + ASSERT_NOT_REACHED(); + return "???"; +} + +static bool isParserRoundTripNumber(const UString& string) +{ + double number = string.toDouble(false, false); + if (isnan(number) || isinf(number)) + return false; + return string == UString::from(number); +} + +// -------- + +SourceStream& SourceStream::operator<<(char c) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = false; + UChar ch(c); + m_string.append(ch); + return *this; +} + +SourceStream& SourceStream::operator<<(const char* s) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = false; + m_string += s; + return *this; +} + +SourceStream& SourceStream::operator<<(double value) +{ + bool needParens = m_numberNeedsParens; + m_numberNeedsParens = false; + m_atStartOfStatement = false; + + if (needParens) + m_string.append('('); + m_string += UString::from(value); + if (needParens) + m_string.append(')'); + + return *this; +} + +SourceStream& SourceStream::operator<<(const UString& s) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = false; + m_string += s; + return *this; +} + +SourceStream& SourceStream::operator<<(const Identifier& s) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = false; + m_string += s.ustring(); + return *this; +} + +SourceStream& SourceStream::operator<<(const Node* n) +{ + bool needParens = (m_precedence != PrecExpression && n->precedence() > m_precedence) || (m_atStartOfStatement && n->needsParensIfLeftmost()); + m_precedence = PrecExpression; + if (!n) + return *this; + if (needParens) { + m_numberNeedsParens = false; + m_string.append('('); + } + n->streamTo(*this); + if (needParens) + m_string.append(')'); + return *this; +} + +SourceStream& SourceStream::operator<<(EndlType) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = true; + m_string.append('\n'); + m_string.append(m_spacesForIndentation); + return *this; +} + +SourceStream& SourceStream::operator<<(IndentType) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = false; + m_spacesForIndentation += " "; + return *this; +} + +SourceStream& SourceStream::operator<<(UnindentType) +{ + m_numberNeedsParens = false; + m_atStartOfStatement = false; + m_spacesForIndentation = m_spacesForIndentation.substr(0, m_spacesForIndentation.size() - 2); + return *this; +} + +inline SourceStream& SourceStream::operator<<(DotExprType) +{ + m_numberNeedsParens = true; + return *this; +} + +inline SourceStream& SourceStream::operator<<(Precedence precedence) +{ + m_precedence = precedence; + return *this; +} + +static void streamLeftAssociativeBinaryOperator(SourceStream& s, Precedence precedence, + const char* operatorString, const Node* left, const Node* right) +{ + s << precedence << left + << ' ' << operatorString << ' ' + << static_cast<Precedence>(precedence - 1) << right; +} + +template <typename T> static inline void streamLeftAssociativeBinaryOperator(SourceStream& s, + Precedence p, const char* o, const RefPtr<T>& l, const RefPtr<T>& r) +{ + streamLeftAssociativeBinaryOperator(s, p, o, l.get(), r.get()); +} + +static inline void bracketNodeStreamTo(SourceStream& s, const RefPtr<ExpressionNode>& base, const RefPtr<ExpressionNode>& subscript) +{ + s << PrecCall << base.get() << "[" << subscript.get() << "]"; +} + +static inline void dotNodeStreamTo(SourceStream& s, const RefPtr<ExpressionNode>& base, const Identifier& ident) +{ + s << DotExpr << PrecCall << base.get() << "." << ident; +} + +// -------- + +UString Node::toString() const +{ + SourceStream stream; + streamTo(stream); + return stream.toString(); +} + +// -------- + +void NullNode::streamTo(SourceStream& s) const +{ + s << "null"; +} + +void FalseNode::streamTo(SourceStream& s) const +{ + s << "false"; +} + +void TrueNode::streamTo(SourceStream& s) const +{ + s << "true"; +} + +void PlaceholderTrueNode::streamTo(SourceStream&) const +{ +} + +void NumberNode::streamTo(SourceStream& s) const +{ + s << value(); +} + +void StringNode::streamTo(SourceStream& s) const +{ + s << '"' << escapeStringForPrettyPrinting(m_value) << '"'; +} + +void RegExpNode::streamTo(SourceStream& s) const +{ + s << '/' << m_regExp->pattern() << '/' << m_regExp->flags(); +} + +void ThisNode::streamTo(SourceStream& s) const +{ + s << "this"; +} + +void ResolveNode::streamTo(SourceStream& s) const +{ + s << m_ident; +} + +void ElementNode::streamTo(SourceStream& s) const +{ + for (const ElementNode* n = this; n; n = n->m_next.get()) { + for (int i = 0; i < n->m_elision; i++) + s << ','; + s << PrecAssignment << n->m_node; + if (n->m_next) + s << ','; + } +} + +void ArrayNode::streamTo(SourceStream& s) const +{ + s << '[' << m_element; + for (int i = 0; i < m_elision; i++) + s << ','; + // Parser consumes one elision comma if there's array elements + // present in the expression. + if (m_optional && m_element) + s << ','; + s << ']'; +} + +void ObjectLiteralNode::streamTo(SourceStream& s) const +{ + if (m_list) + s << "{ " << m_list << " }"; + else + s << "{ }"; +} + +void PropertyListNode::streamTo(SourceStream& s) const +{ + s << m_node; + for (const PropertyListNode* n = m_next.get(); n; n = n->m_next.get()) + s << ", " << n->m_node; +} + +void PropertyNode::streamTo(SourceStream& s) const +{ + switch (m_type) { + case Constant: { + UString propertyName = name().ustring(); + if (isParserRoundTripNumber(propertyName)) + s << propertyName; + else + s << '"' << escapeStringForPrettyPrinting(propertyName) << '"'; + s << ": " << PrecAssignment << m_assign; + break; + } + case Getter: + case Setter: { + const FuncExprNode* func = static_cast<const FuncExprNode*>(m_assign.get()); + if (m_type == Getter) + s << "get "; + else + s << "set "; + s << escapeStringForPrettyPrinting(name().ustring()) + << "(" << func->m_parameter << ')' << func->m_body; + break; + } + } +} + +void BracketAccessorNode::streamTo(SourceStream& s) const +{ + bracketNodeStreamTo(s, m_base, m_subscript); +} + +void DotAccessorNode::streamTo(SourceStream& s) const +{ + dotNodeStreamTo(s, m_base, m_ident); +} + +void ArgumentListNode::streamTo(SourceStream& s) const +{ + s << PrecAssignment << m_expr; + for (ArgumentListNode* n = m_next.get(); n; n = n->m_next.get()) + s << ", " << PrecAssignment << n->m_expr; +} + +void ArgumentsNode::streamTo(SourceStream& s) const +{ + s << '(' << m_listNode << ')'; +} + +void NewExprNode::streamTo(SourceStream& s) const +{ + s << "new " << PrecMember << m_expr << m_args; +} + +void FunctionCallValueNode::streamTo(SourceStream& s) const +{ + s << PrecCall << m_expr << m_args; +} + +void FunctionCallResolveNode::streamTo(SourceStream& s) const +{ + s << m_ident << m_args; +} + +void FunctionCallBracketNode::streamTo(SourceStream& s) const +{ + bracketNodeStreamTo(s, m_base, m_subscript); + s << m_args; +} + +void FunctionCallDotNode::streamTo(SourceStream& s) const +{ + dotNodeStreamTo(s, m_base, m_ident); + s << m_args; +} + +void PostIncResolveNode::streamTo(SourceStream& s) const +{ + s << m_ident << "++"; +} + +void PostDecResolveNode::streamTo(SourceStream& s) const +{ + s << m_ident << "--"; +} + +void PostIncBracketNode::streamTo(SourceStream& s) const +{ + bracketNodeStreamTo(s, m_base, m_subscript); + s << "++"; +} + +void PostDecBracketNode::streamTo(SourceStream& s) const +{ + bracketNodeStreamTo(s, m_base, m_subscript); + s << "--"; +} + +void PostIncDotNode::streamTo(SourceStream& s) const +{ + dotNodeStreamTo(s, m_base, m_ident); + s << "++"; +} + +void PostDecDotNode::streamTo(SourceStream& s) const +{ + dotNodeStreamTo(s, m_base, m_ident); + s << "--"; +} + +void PostfixErrorNode::streamTo(SourceStream& s) const +{ + s << PrecLeftHandSide << m_expr; + if (m_operator == OpPlusPlus) + s << "++"; + else + s << "--"; +} + +void DeleteResolveNode::streamTo(SourceStream& s) const +{ + s << "delete " << m_ident; +} + +void DeleteBracketNode::streamTo(SourceStream& s) const +{ + s << "delete "; + bracketNodeStreamTo(s, m_base, m_subscript); +} + +void DeleteDotNode::streamTo(SourceStream& s) const +{ + s << "delete "; + dotNodeStreamTo(s, m_base, m_ident); +} + +void DeleteValueNode::streamTo(SourceStream& s) const +{ + s << "delete " << PrecUnary << m_expr; +} + +void VoidNode::streamTo(SourceStream& s) const +{ + s << "void " << PrecUnary << m_expr; +} + +void TypeOfValueNode::streamTo(SourceStream& s) const +{ + s << "typeof " << PrecUnary << m_expr; +} + +void TypeOfResolveNode::streamTo(SourceStream& s) const +{ + s << "typeof " << m_ident; +} + +void PreIncResolveNode::streamTo(SourceStream& s) const +{ + s << "++" << m_ident; +} + +void PreDecResolveNode::streamTo(SourceStream& s) const +{ + s << "--" << m_ident; +} + +void PreIncBracketNode::streamTo(SourceStream& s) const +{ + s << "++"; + bracketNodeStreamTo(s, m_base, m_subscript); +} + +void PreDecBracketNode::streamTo(SourceStream& s) const +{ + s << "--"; + bracketNodeStreamTo(s, m_base, m_subscript); +} + +void PreIncDotNode::streamTo(SourceStream& s) const +{ + s << "++"; + dotNodeStreamTo(s, m_base, m_ident); +} + +void PreDecDotNode::streamTo(SourceStream& s) const +{ + s << "--"; + dotNodeStreamTo(s, m_base, m_ident); +} + +void PrefixErrorNode::streamTo(SourceStream& s) const +{ + if (m_operator == OpPlusPlus) + s << "++" << PrecUnary << m_expr; + else + s << "--" << PrecUnary << m_expr; +} + +void UnaryPlusNode::streamTo(SourceStream& s) const +{ + s << "+ " << PrecUnary << m_expr; +} + +void NegateNode::streamTo(SourceStream& s) const +{ + s << "- " << PrecUnary << m_expr; +} + +void BitwiseNotNode::streamTo(SourceStream& s) const +{ + s << "~" << PrecUnary << m_expr; +} + +void LogicalNotNode::streamTo(SourceStream& s) const +{ + s << "!" << PrecUnary << m_expr; +} + +void MultNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "*", m_term1, m_term2); +} + +void DivNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "/", m_term1, m_term2); +} + +void ModNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "%", m_term1, m_term2); +} + +void AddNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "+", m_term1, m_term2); +} + +void SubNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "-", m_term1, m_term2); +} + +void LeftShiftNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "<<", m_term1, m_term2); +} + +void RightShiftNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), ">>", m_term1, m_term2); +} + +void UnsignedRightShiftNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), ">>>", m_term1, m_term2); +} + +void LessNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "<", m_expr1, m_expr2); +} + +void GreaterNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), ">", m_expr1, m_expr2); +} + +void LessEqNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "<=", m_expr1, m_expr2); +} + +void GreaterEqNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), ">=", m_expr1, m_expr2); +} + +void InstanceOfNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "instanceof", m_expr1, m_expr2); +} + +void InNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "in", m_expr1, m_expr2); +} + +void EqualNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "==", m_expr1, m_expr2); +} + +void NotEqualNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "!=", m_expr1, m_expr2); +} + +void StrictEqualNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "===", m_expr1, m_expr2); +} + +void NotStrictEqualNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "!==", m_expr1, m_expr2); +} + +void BitAndNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "&", m_expr1, m_expr2); +} + +void BitXOrNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "^", m_expr1, m_expr2); +} + +void BitOrNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "|", m_expr1, m_expr2); +} + +void LogicalAndNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "&&", m_expr1, m_expr2); +} + +void LogicalOrNode::streamTo(SourceStream& s) const +{ + streamLeftAssociativeBinaryOperator(s, precedence(), "||", m_expr1, m_expr2); +} + +void ConditionalNode::streamTo(SourceStream& s) const +{ + s << PrecLogicalOr << m_logical + << " ? " << PrecAssignment << m_expr1 + << " : " << PrecAssignment << m_expr2; +} + +void ReadModifyResolveNode::streamTo(SourceStream& s) const +{ + s << m_ident << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; +} + +void AssignResolveNode::streamTo(SourceStream& s) const +{ + s << m_ident << " = " << PrecAssignment << m_right; +} + +void ReadModifyBracketNode::streamTo(SourceStream& s) const +{ + bracketNodeStreamTo(s, m_base, m_subscript); + s << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; +} + +void AssignBracketNode::streamTo(SourceStream& s) const +{ + bracketNodeStreamTo(s, m_base, m_subscript); + s << " = " << PrecAssignment << m_right; +} + +void ReadModifyDotNode::streamTo(SourceStream& s) const +{ + dotNodeStreamTo(s, m_base, m_ident); + s << ' ' << operatorString(m_operator) << ' ' << PrecAssignment << m_right; +} + +void AssignDotNode::streamTo(SourceStream& s) const +{ + dotNodeStreamTo(s, m_base, m_ident); + s << " = " << PrecAssignment << m_right; +} + +void AssignErrorNode::streamTo(SourceStream& s) const +{ + s << PrecLeftHandSide << m_left << ' ' + << operatorString(m_operator) << ' ' << PrecAssignment << m_right; +} + +void CommaNode::streamTo(SourceStream& s) const +{ + s << PrecAssignment << m_expr1 << ", " << PrecAssignment << m_expr2; +} + +void ConstDeclNode::streamTo(SourceStream& s) const +{ + s << m_ident; + if (m_init) + s << " = " << m_init; + for (ConstDeclNode* n = m_next.get(); n; n = n->m_next.get()) { + s << ", " << m_ident; + if (m_init) + s << " = " << m_init; + } +} + +void ConstStatementNode::streamTo(SourceStream& s) const +{ + s << Endl << "const " << m_next << ';'; +} + +static inline void statementListStreamTo(const Vector<RefPtr<StatementNode> >& nodes, SourceStream& s) +{ + for (Vector<RefPtr<StatementNode> >::const_iterator ptr = nodes.begin(); ptr != nodes.end(); ptr++) + s << *ptr; +} + +void BlockNode::streamTo(SourceStream& s) const +{ + s << Endl << "{" << Indent; + statementListStreamTo(m_children, s); + s << Unindent << Endl << "}"; +} + +void ScopeNode::streamTo(SourceStream& s) const +{ + s << Endl << "{" << Indent; + + bool printedVar = false; + for (size_t i = 0; i < m_varStack.size(); ++i) { + if (m_varStack[i].second == 0) { + if (!printedVar) { + s << Endl << "var "; + printedVar = true; + } else + s << ", "; + s << m_varStack[i].first; + } + } + if (printedVar) + s << ';'; + + statementListStreamTo(m_children, s); + s << Unindent << Endl << "}"; +} + +void EmptyStatementNode::streamTo(SourceStream& s) const +{ + s << Endl << ';'; +} + +void ExprStatementNode::streamTo(SourceStream& s) const +{ + s << Endl << m_expr << ';'; +} + +void VarStatementNode::streamTo(SourceStream& s) const +{ + s << Endl << "var " << m_expr << ';'; +} + +void IfNode::streamTo(SourceStream& s) const +{ + s << Endl << "if (" << m_condition << ')' << Indent << m_ifBlock << Unindent; +} + +void IfElseNode::streamTo(SourceStream& s) const +{ + IfNode::streamTo(s); + s << Endl << "else" << Indent << m_elseBlock << Unindent; +} + +void DoWhileNode::streamTo(SourceStream& s) const +{ + s << Endl << "do " << Indent << m_statement << Unindent << Endl + << "while (" << m_expr << ");"; +} + +void WhileNode::streamTo(SourceStream& s) const +{ + s << Endl << "while (" << m_expr << ')' << Indent << m_statement << Unindent; +} + +void ForNode::streamTo(SourceStream& s) const +{ + s << Endl << "for (" + << (m_expr1WasVarDecl ? "var " : "") + << m_expr1 + << "; " << m_expr2 + << "; " << m_expr3 + << ')' << Indent << m_statement << Unindent; +} + +void ForInNode::streamTo(SourceStream& s) const +{ + s << Endl << "for ("; + if (m_identIsVarDecl) { + s << "var "; + if (m_init) + s << m_init; + else + s << PrecLeftHandSide << m_lexpr; + } else + s << PrecLeftHandSide << m_lexpr; + + s << " in " << m_expr << ')' << Indent << m_statement << Unindent; +} + +void ContinueNode::streamTo(SourceStream& s) const +{ + s << Endl << "continue"; + if (!m_ident.isNull()) + s << ' ' << m_ident; + s << ';'; +} + +void BreakNode::streamTo(SourceStream& s) const +{ + s << Endl << "break"; + if (!m_ident.isNull()) + s << ' ' << m_ident; + s << ';'; +} + +void ReturnNode::streamTo(SourceStream& s) const +{ + s << Endl << "return"; + if (m_value) + s << ' ' << m_value; + s << ';'; +} + +void WithNode::streamTo(SourceStream& s) const +{ + s << Endl << "with (" << m_expr << ") " << m_statement; +} + +void CaseClauseNode::streamTo(SourceStream& s) const +{ + s << Endl; + if (m_expr) + s << "case " << m_expr; + else + s << "default"; + s << ":" << Indent; + statementListStreamTo(m_children, s); + s << Unindent; +} + +void ClauseListNode::streamTo(SourceStream& s) const +{ + for (const ClauseListNode* n = this; n; n = n->getNext()) + s << n->getClause(); +} + +void CaseBlockNode::streamTo(SourceStream& s) const +{ + for (const ClauseListNode* n = m_list1.get(); n; n = n->getNext()) + s << n->getClause(); + s << m_defaultClause; + for (const ClauseListNode* n = m_list2.get(); n; n = n->getNext()) + s << n->getClause(); +} + +void SwitchNode::streamTo(SourceStream& s) const +{ + s << Endl << "switch (" << m_expr << ") {" + << Indent << m_block << Unindent + << Endl << "}"; +} + +void LabelNode::streamTo(SourceStream& s) const +{ + s << Endl << m_label << ":" << Indent << m_statement << Unindent; +} + +void ThrowNode::streamTo(SourceStream& s) const +{ + s << Endl << "throw " << m_expr << ';'; +} + +void TryNode::streamTo(SourceStream& s) const +{ + s << Endl << "try " << m_tryBlock; + if (m_catchBlock) + s << Endl << "catch (" << m_exceptionIdent << ')' << m_catchBlock; + if (m_finallyBlock) + s << Endl << "finally " << m_finallyBlock; +} + +void ParameterNode::streamTo(SourceStream& s) const +{ + s << m_ident; + for (ParameterNode* n = m_next.get(); n; n = n->m_next.get()) + s << ", " << n->m_ident; +} + +void FuncDeclNode::streamTo(SourceStream& s) const +{ + s << Endl << "function " << m_ident << '(' << m_parameter << ')' << m_body; +} + +void FuncExprNode::streamTo(SourceStream& s) const +{ + s << "function " << m_ident << '(' << m_parameter << ')' << m_body; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/number_object.cpp b/JavaScriptCore/kjs/number_object.cpp new file mode 100644 index 0000000..94fc7e7 --- /dev/null +++ b/JavaScriptCore/kjs/number_object.cpp @@ -0,0 +1,522 @@ +/* + * 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 "number_object.h" +#include "number_object.lut.h" + +#include "dtoa.h" +#include "error_object.h" +#include "operations.h" +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> + +namespace KJS { + +// ------------------------------ NumberInstance ---------------------------- + +const ClassInfo NumberInstance::info = { "Number", 0, 0 }; + +NumberInstance::NumberInstance(JSObject* proto) + : JSWrapperObject(proto) +{ +} + +// ------------------------------ NumberPrototype --------------------------- + +static JSValue* numberProtoFuncToString(ExecState*, JSObject*, const List&); +static JSValue* numberProtoFuncToLocaleString(ExecState*, JSObject*, const List&); +static JSValue* numberProtoFuncValueOf(ExecState*, JSObject*, const List&); +static JSValue* numberProtoFuncToFixed(ExecState*, JSObject*, const List&); +static JSValue* numberProtoFuncToExponential(ExecState*, JSObject*, const List&); +static JSValue* numberProtoFuncToPrecision(ExecState*, JSObject*, const List&); + +// ECMA 15.7.4 + +NumberPrototype::NumberPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype) + : NumberInstance(objectPrototype) +{ + setInternalValue(jsNumber(0)); + + // The constructor will be added later, after NumberObjectImp has been constructed + + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toString, numberProtoFuncToString), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toLocaleString, numberProtoFuncToLocaleString), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().valueOf, numberProtoFuncValueOf), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toFixed, numberProtoFuncToFixed), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toExponential, numberProtoFuncToExponential), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toPrecision, numberProtoFuncToPrecision), DontEnum); +} + +// ------------------------------ Functions --------------------------- + +// ECMA 15.7.4.2 - 15.7.4.7 + +static UString integer_part_noexp(double d) +{ + int decimalPoint; + int sign; + char* result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL); + bool resultIsInfOrNan = (decimalPoint == 9999); + size_t length = strlen(result); + + UString str = sign ? "-" : ""; + if (resultIsInfOrNan) + str += result; + else if (decimalPoint <= 0) + str += "0"; + else { + Vector<char, 1024> buf(decimalPoint + 1); + + if (static_cast<int>(length) <= decimalPoint) { + strcpy(buf.data(), result); + memset(buf.data() + length, '0', decimalPoint - length); + } else + strncpy(buf.data(), result, decimalPoint); + + buf[decimalPoint] = '\0'; + str += UString(buf.data()); + } + + kjs_freedtoa(result); + + return str; +} + +static UString char_sequence(char c, int count) +{ + Vector<char, 2048> buf(count + 1, c); + buf[count] = '\0'; + + return UString(buf.data()); +} + +static double intPow10(int e) +{ + // This function uses the "exponentiation by squaring" algorithm and + // long double to quickly and precisely calculate integer powers of 10.0. + + // This is a handy workaround for <rdar://problem/4494756> + + if (e == 0) + return 1.0; + + bool negative = e < 0; + unsigned exp = negative ? -e : e; + + long double result = 10.0; + bool foundOne = false; + for (int bit = 31; bit >= 0; bit--) { + if (!foundOne) { + if ((exp >> bit) & 1) + foundOne = true; + } else { + result = result * result; + if ((exp >> bit) & 1) + result = result * 10.0; + } + } + + if (negative) + return static_cast<double>(1.0 / result); + return static_cast<double>(result); +} + + +JSValue* numberProtoFuncToString(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&NumberInstance::info)) + return throwError(exec, TypeError); + + JSValue* v = static_cast<NumberInstance*>(thisObj)->internalValue(); + + double radixAsDouble = args[0]->toInteger(exec); // nan -> 0 + if (radixAsDouble == 10 || args[0]->isUndefined()) + return jsString(v->toString(exec)); + + if (radixAsDouble < 2 || radixAsDouble > 36) + return throwError(exec, RangeError, "toString() radix argument must be between 2 and 36"); + + int radix = static_cast<int>(radixAsDouble); + const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + // INT_MAX results in 1024 characters left of the dot with radix 2 + // give the same space on the right side. safety checks are in place + // unless someone finds a precise rule. + char s[2048 + 3]; + const char* lastCharInString = s + sizeof(s) - 1; + double x = v->toNumber(exec); + if (isnan(x) || isinf(x)) + return jsString(UString::from(x)); + + bool isNegative = x < 0.0; + if (isNegative) + x = -x; + + double integerPart = floor(x); + char* decimalPoint = s + sizeof(s) / 2; + + // convert integer portion + char* p = decimalPoint; + double d = integerPart; + do { + int remainderDigit = static_cast<int>(fmod(d, radix)); + *--p = digits[remainderDigit]; + d /= radix; + } while ((d <= -1.0 || d >= 1.0) && s < p); + + if (isNegative) + *--p = '-'; + char* startOfResultString = p; + ASSERT(s <= startOfResultString); + + d = x - integerPart; + p = decimalPoint; + const double epsilon = 0.001; // TODO: guessed. base on radix ? + bool hasFractionalPart = (d < -epsilon || d > epsilon); + if (hasFractionalPart) { + *p++ = '.'; + do { + d *= radix; + const int digit = static_cast<int>(d); + *p++ = digits[digit]; + d -= digit; + } while ((d < -epsilon || d > epsilon) && p < lastCharInString); + } + *p = '\0'; + ASSERT(p < s + sizeof(s)); + + return jsString(startOfResultString); +} + +JSValue* numberProtoFuncToLocaleString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&NumberInstance::info)) + return throwError(exec, TypeError); + + // TODO + return jsString(static_cast<NumberInstance*>(thisObj)->internalValue()->toString(exec)); +} + +JSValue* numberProtoFuncValueOf(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&NumberInstance::info)) + return throwError(exec, TypeError); + + return static_cast<NumberInstance*>(thisObj)->internalValue()->toJSNumber(exec); +} + +JSValue* numberProtoFuncToFixed(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&NumberInstance::info)) + return throwError(exec, TypeError); + + JSValue* v = static_cast<NumberInstance*>(thisObj)->internalValue(); + + JSValue* fractionDigits = args[0]; + double df = fractionDigits->toInteger(exec); + if (!(df >= 0 && df <= 20)) + return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20"); + int f = (int)df; + + double x = v->toNumber(exec); + if (isnan(x)) + return jsString("NaN"); + + UString s; + if (x < 0) { + s.append('-'); + x = -x; + } else if (x == -0.0) + x = 0; + + if (x >= pow(10.0, 21.0)) + return jsString(s + UString::from(x)); + + const double tenToTheF = pow(10.0, f); + double n = floor(x * tenToTheF); + if (fabs(n / tenToTheF - x) >= fabs((n + 1) / tenToTheF - x)) + n++; + + UString m = integer_part_noexp(n); + + int k = m.size(); + if (k <= f) { + UString z; + for (int i = 0; i < f + 1 - k; i++) + z.append('0'); + m = z + m; + k = f + 1; + ASSERT(k == m.size()); + } + int kMinusf = k - f; + if (kMinusf < m.size()) + return jsString(s + m.substr(0, kMinusf) + "." + m.substr(kMinusf)); + return jsString(s + m.substr(0, kMinusf)); +} + +static void fractionalPartToString(char* buf, int& i, const char* result, int resultLength, int fractionalDigits) +{ + if (fractionalDigits <= 0) + return; + + int fDigitsInResult = static_cast<int>(resultLength) - 1; + buf[i++] = '.'; + if (fDigitsInResult > 0) { + if (fractionalDigits < fDigitsInResult) { + strncpy(buf + i, result + 1, fractionalDigits); + i += fractionalDigits; + } else { + strcpy(buf + i, result + 1); + i += static_cast<int>(resultLength) - 1; + } + } + + for (int j = 0; j < fractionalDigits - fDigitsInResult; j++) + buf[i++] = '0'; +} + +static void exponentialPartToString(char* buf, int& i, int decimalPoint) +{ + buf[i++] = 'e'; + buf[i++] = (decimalPoint >= 0) ? '+' : '-'; + // decimalPoint can't be more than 3 digits decimal given the + // nature of float representation + int exponential = decimalPoint - 1; + if (exponential < 0) + exponential *= -1; + if (exponential >= 100) + buf[i++] = static_cast<char>('0' + exponential / 100); + if (exponential >= 10) + buf[i++] = static_cast<char>('0' + (exponential % 100) / 10); + buf[i++] = static_cast<char>('0' + exponential % 10); +} + +JSValue* numberProtoFuncToExponential(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&NumberInstance::info)) + return throwError(exec, TypeError); + + JSValue* v = static_cast<NumberInstance*>(thisObj)->internalValue(); + + double x = v->toNumber(exec); + + if (isnan(x) || isinf(x)) + return jsString(UString::from(x)); + + JSValue* fractionalDigitsValue = args[0]; + double df = fractionalDigitsValue->toInteger(exec); + if (!(df >= 0 && df <= 20)) + return throwError(exec, RangeError, "toExponential() argument must between 0 and 20"); + int fractionalDigits = (int)df; + bool includeAllDigits = fractionalDigitsValue->isUndefined(); + + int decimalAdjust = 0; + if (x && !includeAllDigits) { + double logx = floor(log10(fabs(x))); + x /= pow(10.0, logx); + const double tenToTheF = pow(10.0, fractionalDigits); + double fx = floor(x * tenToTheF) / tenToTheF; + double cx = ceil(x * tenToTheF) / tenToTheF; + + if (fabs(fx - x) < fabs(cx - x)) + x = fx; + else + x = cx; + + decimalAdjust = static_cast<int>(logx); + } + + if (isnan(x)) + return jsString("NaN"); + + if (x == -0.0) // (-0.0).toExponential() should print as 0 instead of -0 + x = 0; + + int decimalPoint; + int sign; + char* result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL); + size_t resultLength = strlen(result); + decimalPoint += decimalAdjust; + + int i = 0; + char buf[80]; // digit + '.' + fractionDigits (max 20) + 'e' + sign + exponent (max?) + if (sign) + buf[i++] = '-'; + + if (decimalPoint == 999) // ? 9999 is the magical "result is Inf or NaN" value. what's 999?? + strcpy(buf + i, result); + else { + buf[i++] = result[0]; + + if (includeAllDigits) + fractionalDigits = static_cast<int>(resultLength) - 1; + + fractionalPartToString(buf, i, result, resultLength, fractionalDigits); + exponentialPartToString(buf, i, decimalPoint); + buf[i++] = '\0'; + } + ASSERT(i <= 80); + + kjs_freedtoa(result); + + return jsString(buf); +} + +JSValue* numberProtoFuncToPrecision(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&NumberInstance::info)) + return throwError(exec, TypeError); + + JSValue* v = static_cast<NumberInstance*>(thisObj)->internalValue(); + + double doublePrecision = args[0]->toIntegerPreserveNaN(exec); + double x = v->toNumber(exec); + if (args[0]->isUndefined() || isnan(x) || isinf(x)) + return jsString(v->toString(exec)); + + UString s; + if (x < 0) { + s = "-"; + x = -x; + } + + if (!(doublePrecision >= 1 && doublePrecision <= 21)) // true for NaN + return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21"); + int precision = (int)doublePrecision; + + int e = 0; + UString m; + if (x) { + e = static_cast<int>(log10(x)); + double tens = intPow10(e - precision + 1); + double n = floor(x / tens); + if (n < intPow10(precision - 1)) { + e = e - 1; + tens = intPow10(e - precision + 1); + n = floor(x / tens); + } + + if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x)) + ++n; + // maintain n < 10^(precision) + if (n >= intPow10(precision)) { + n /= 10.0; + e += 1; + } + ASSERT(intPow10(precision - 1) <= n); + ASSERT(n < intPow10(precision)); + + m = integer_part_noexp(n); + if (e < -6 || e >= precision) { + if (m.size() > 1) + m = m.substr(0, 1) + "." + m.substr(1); + if (e >= 0) + return jsString(s + m + "e+" + UString::from(e)); + return jsString(s + m + "e-" + UString::from(-e)); + } + } else { + m = char_sequence('0', precision); + e = 0; + } + + if (e == precision - 1) + return jsString(s + m); + if (e >= 0) { + if (e + 1 < m.size()) + return jsString(s + m.substr(0, e + 1) + "." + m.substr(e + 1)); + return jsString(s + m); + } + return jsString(s + "0." + char_sequence('0', -(e + 1)) + m); +} + +// ------------------------------ NumberObjectImp ------------------------------ + +const ClassInfo NumberObjectImp::info = { "Function", &InternalFunctionImp::info, &numberTable }; + +/* Source for number_object.lut.h +@begin numberTable 5 + NaN NumberObjectImp::NaNValue DontEnum|DontDelete|ReadOnly + NEGATIVE_INFINITY NumberObjectImp::NegInfinity DontEnum|DontDelete|ReadOnly + POSITIVE_INFINITY NumberObjectImp::PosInfinity DontEnum|DontDelete|ReadOnly + MAX_VALUE NumberObjectImp::MaxValue DontEnum|DontDelete|ReadOnly + MIN_VALUE NumberObjectImp::MinValue DontEnum|DontDelete|ReadOnly +@end +*/ +NumberObjectImp::NumberObjectImp(ExecState* exec, FunctionPrototype* funcProto, NumberPrototype* numberProto) + : InternalFunctionImp(funcProto, numberProto->classInfo()->className) +{ + // Number.Prototype + putDirect(exec->propertyNames().prototype, numberProto, DontEnum|DontDelete|ReadOnly); + + // no. of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); +} + +bool NumberObjectImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<NumberObjectImp, InternalFunctionImp>(exec, &numberTable, this, propertyName, slot); +} + +JSValue* NumberObjectImp::getValueProperty(ExecState*, int token) const +{ + // ECMA 15.7.3 + switch (token) { + case NaNValue: + return jsNaN(); + case NegInfinity: + return jsNumberCell(-Inf); + case PosInfinity: + return jsNumberCell(Inf); + case MaxValue: + return jsNumberCell(1.7976931348623157E+308); + case MinValue: + return jsNumberCell(5E-324); + } + ASSERT_NOT_REACHED(); + return jsNull(); +} + +bool NumberObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.7.1 +JSObject* NumberObjectImp::construct(ExecState* exec, const List& args) +{ + JSObject* proto = exec->lexicalGlobalObject()->numberPrototype(); + NumberInstance* obj = new NumberInstance(proto); + + // FIXME: Check args[0]->isUndefined() instead of args.isEmpty()? + double n = args.isEmpty() ? 0 : args[0]->toNumber(exec); + obj->setInternalValue(jsNumber(n)); + return obj; +} + +// ECMA 15.7.2 +JSValue* NumberObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + // FIXME: Check args[0]->isUndefined() instead of args.isEmpty()? + return jsNumber(args.isEmpty() ? 0 : args[0]->toNumber(exec)); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/number_object.h b/JavaScriptCore/kjs/number_object.h new file mode 100644 index 0000000..36befed --- /dev/null +++ b/JavaScriptCore/kjs/number_object.h @@ -0,0 +1,76 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NUMBER_OBJECT_H_ +#define NUMBER_OBJECT_H_ + +#include "function_object.h" +#include "JSWrapperObject.h" + +namespace KJS { + + class NumberInstance : public JSWrapperObject { + public: + NumberInstance(JSObject* prototype); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + /** + * @internal + * + * The initial value of Number.prototype (and thus all objects created + * with the Number constructor + */ + class NumberPrototype : public NumberInstance { + public: + NumberPrototype(ExecState*, ObjectPrototype*, FunctionPrototype*); + }; + + /** + * @internal + * + * The initial value of the the global variable's "Number" property + */ + class NumberObjectImp : public InternalFunctionImp { + public: + NumberObjectImp(ExecState*, FunctionPrototype*, NumberPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + + bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + JSValue* getValueProperty(ExecState*, int token) const; + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + enum { NaNValue, NegInfinity, PosInfinity, MaxValue, MinValue }; + + JSObject* construct(const List&); + }; + +} // namespace KJS + +#endif // NUMBER_OBJECT_H_ diff --git a/JavaScriptCore/kjs/object.cpp b/JavaScriptCore/kjs/object.cpp new file mode 100644 index 0000000..9f1c2cf --- /dev/null +++ b/JavaScriptCore/kjs/object.cpp @@ -0,0 +1,660 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * 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 "object.h" + +#include "date_object.h" +#include "error_object.h" +#include "lookup.h" +#include "nodes.h" +#include "operations.h" +#include "PropertyNameArray.h" +#include <math.h> +#include <wtf/Assertions.h> + +// maximum global call stack size. Protects against accidental or +// malicious infinite recursions. Define to -1 if you want no limit. +// In real-world testing it appears ok to bump the stack depth count to 500. +// This of course is dependent on stack frame size. +#define KJS_MAX_STACK 500 + +#define JAVASCRIPT_CALL_TRACING 0 +#define JAVASCRIPT_MARK_TRACING 0 + +#if JAVASCRIPT_CALL_TRACING +static bool _traceJavaScript = false; + +extern "C" { + void setTraceJavaScript(bool f) + { + _traceJavaScript = f; + } + + static bool traceJavaScript() + { + return _traceJavaScript; + } +} +#endif + +namespace KJS { + +// ------------------------------ Object --------------------------------------- + +JSValue *JSObject::call(ExecState *exec, JSObject *thisObj, const List &args) +{ + ASSERT(implementsCall()); + +#if KJS_MAX_STACK > 0 + static int depth = 0; // sum of all extant function calls + +#if JAVASCRIPT_CALL_TRACING + static bool tracing = false; + if (traceJavaScript() && !tracing) { + tracing = true; + for (int i = 0; i < depth; i++) + putchar (' '); + printf ("*** calling: %s\n", toString(exec).ascii()); + for (int j = 0; j < args.size(); j++) { + for (int i = 0; i < depth; i++) + putchar (' '); + printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii()); + } + tracing = false; + } +#endif + + if (++depth > KJS_MAX_STACK) { + --depth; + return throwError(exec, RangeError, "Maximum call stack size exceeded."); + } +#endif + + JSValue *ret = callAsFunction(exec,thisObj,args); + +#if KJS_MAX_STACK > 0 + --depth; +#endif + +#if JAVASCRIPT_CALL_TRACING + if (traceJavaScript() && !tracing) { + tracing = true; + for (int i = 0; i < depth; i++) + putchar (' '); + printf ("*** returning: %s\n", ret->toString(exec).ascii()); + tracing = false; + } +#endif + + return ret; +} + +// ------------------------------ JSObject ------------------------------------ + +void JSObject::mark() +{ + JSCell::mark(); + +#if JAVASCRIPT_MARK_TRACING + static int markStackDepth = 0; + markStackDepth++; + for (int i = 0; i < markStackDepth; i++) + putchar('-'); + + printf("%s (%p)\n", className().UTF8String().c_str(), this); +#endif + + JSValue *proto = _proto; + if (!proto->marked()) + proto->mark(); + + _prop.mark(); + +#if JAVASCRIPT_MARK_TRACING + markStackDepth--; +#endif +} + +JSType JSObject::type() const +{ + return ObjectType; +} + +const ClassInfo *JSObject::classInfo() const +{ + return 0; +} + +UString JSObject::className() const +{ + const ClassInfo *ci = classInfo(); + if ( ci ) + return ci->className; + return "Object"; +} + +JSValue *JSObject::get(ExecState *exec, const Identifier &propertyName) const +{ + PropertySlot slot; + + if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, const_cast<JSObject *>(this), propertyName); + + return jsUndefined(); +} + +JSValue *JSObject::get(ExecState *exec, unsigned propertyName) const +{ + PropertySlot slot; + if (const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, const_cast<JSObject *>(this), propertyName); + + return jsUndefined(); +} + +bool JSObject::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot) +{ + JSObject *imp = this; + + while (true) { + if (imp->getOwnPropertySlot(exec, propertyName, slot)) + return true; + + JSValue *proto = imp->_proto; + if (!proto->isObject()) + break; + + imp = static_cast<JSObject *>(proto); + } + + return false; +} + +bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot) +{ + return getOwnPropertySlot(exec, Identifier::from(propertyName), slot); +} + +static void throwSetterError(ExecState *exec) +{ + throwError(exec, TypeError, "setting a property that has only a getter"); +} + +// ECMA 8.6.2.2 +void JSObject::put(ExecState* exec, const Identifier &propertyName, JSValue *value) +{ + ASSERT(value); + + if (propertyName == exec->propertyNames().underscoreProto) { + JSObject* proto = value->getObject(); + while (proto) { + if (proto == this) + throwError(exec, GeneralError, "cyclic __proto__ value"); + proto = proto->prototype() ? proto->prototype()->getObject() : 0; + } + + setPrototype(value); + return; + } + + // Check if there are any setters or getters in the prototype chain + JSObject *obj = this; + bool hasGettersOrSetters = false; + while (true) { + if (obj->_prop.hasGetterSetterProperties()) { + hasGettersOrSetters = true; + break; + } + + if (!obj->_proto->isObject()) + break; + + obj = static_cast<JSObject *>(obj->_proto); + } + + if (hasGettersOrSetters) { + unsigned attributes; + if (_prop.get(propertyName, attributes) && attributes & ReadOnly) + return; + + obj = this; + while (true) { + if (JSValue *gs = obj->_prop.get(propertyName, attributes)) { + if (attributes & GetterSetter) { + JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter(); + + if (!setterFunc) { + throwSetterError(exec); + return; + } + + List args; + args.append(value); + + setterFunc->call(exec, this, args); + return; + } else { + // If there's an existing property on the object or one of its + // prototype it should be replaced, so we just break here. + break; + } + } + + if (!obj->_proto->isObject()) + break; + + obj = static_cast<JSObject *>(obj->_proto); + } + } + + _prop.put(propertyName, value, 0, true); +} + +void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value) +{ + put(exec, Identifier::from(propertyName), value); +} + +// ECMA 8.6.2.4 +bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const +{ + PropertySlot slot; + return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot); +} + +bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const +{ + PropertySlot slot; + return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot); +} + +// ECMA 8.6.2.5 +bool JSObject::deleteProperty(ExecState* /*exec*/, const Identifier &propertyName) +{ + unsigned attributes; + JSValue *v = _prop.get(propertyName, attributes); + if (v) { + if ((attributes & DontDelete)) + return false; + _prop.remove(propertyName); + if (attributes & GetterSetter) + _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters()); + return true; + } + + // Look in the static hashtable of properties + const HashEntry* entry = findPropertyHashEntry(propertyName); + if (entry && entry->attr & DontDelete) + return false; // this builtin property can't be deleted + return true; +} + +bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const +{ + PropertySlot slot; + return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot); +} + +bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName) +{ + return deleteProperty(exec, Identifier::from(propertyName)); +} + +static ALWAYS_INLINE JSValue *tryGetAndCallProperty(ExecState *exec, const JSObject *object, const Identifier &propertyName) { + JSValue *v = object->get(exec, propertyName); + if (v->isObject()) { + JSObject *o = static_cast<JSObject*>(v); + if (o->implementsCall()) { // spec says "not primitive type" but ... + JSObject *thisObj = const_cast<JSObject*>(object); + JSValue* def = o->call(exec, thisObj, exec->emptyList()); + JSType defType = def->type(); + ASSERT(defType != GetterSetterType); + if (defType != ObjectType) + return def; + } + } + return NULL; +} + +bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result) +{ + result = defaultValue(exec, NumberType); + number = result->toNumber(exec); + return !result->isString(); +} + +// ECMA 8.6.2.6 +JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const +{ + /* Prefer String for Date objects */ + if ((hint == StringType) || (hint != NumberType && _proto == exec->lexicalGlobalObject()->datePrototype())) { + if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString)) + return v; + if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf)) + return v; + } else { + if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().valueOf)) + return v; + if (JSValue* v = tryGetAndCallProperty(exec, this, exec->propertyNames().toString)) + return v; + } + + if (exec->hadException()) + return exec->exception(); + + return throwError(exec, TypeError, "No default value"); +} + +const HashEntry* JSObject::findPropertyHashEntry(const Identifier& propertyName) const +{ + for (const ClassInfo *info = classInfo(); info; info = info->parentClass) { + if (const HashTable *propHashTable = info->propHashTable) { + if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName)) + return e; + } + } + return 0; +} + +void JSObject::defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc) +{ + JSValue *o = getDirect(propertyName); + GetterSetterImp *gs; + + if (o && o->type() == GetterSetterType) { + gs = static_cast<GetterSetterImp *>(o); + } else { + gs = new GetterSetterImp; + putDirect(propertyName, gs, GetterSetter); + } + + _prop.setHasGetterSetterProperties(true); + gs->setGetter(getterFunc); +} + +void JSObject::defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc) +{ + JSValue *o = getDirect(propertyName); + GetterSetterImp *gs; + + if (o && o->type() == GetterSetterType) { + gs = static_cast<GetterSetterImp *>(o); + } else { + gs = new GetterSetterImp; + putDirect(propertyName, gs, GetterSetter); + } + + _prop.setHasGetterSetterProperties(true); + gs->setSetter(setterFunc); +} + +bool JSObject::implementsConstruct() const +{ + return false; +} + +JSObject* JSObject::construct(ExecState*, const List& /*args*/) +{ + ASSERT(false); + return NULL; +} + +JSObject* JSObject::construct(ExecState* exec, const List& args, const Identifier& /*functionName*/, const UString& /*sourceURL*/, int /*lineNumber*/) +{ + return construct(exec, args); +} + +bool JSObject::implementsCall() const +{ + return false; +} + +JSValue *JSObject::callAsFunction(ExecState* /*exec*/, JSObject* /*thisObj*/, const List &/*args*/) +{ + ASSERT(false); + return NULL; +} + +bool JSObject::implementsHasInstance() const +{ + return false; +} + +bool JSObject::hasInstance(ExecState* exec, JSValue* value) +{ + JSValue* proto = get(exec, exec->propertyNames().prototype); + if (!proto->isObject()) { + throwError(exec, TypeError, "intanceof called on an object with an invalid prototype property."); + return false; + } + + if (!value->isObject()) + return false; + + JSObject* o = static_cast<JSObject*>(value); + while ((o = o->prototype()->getObject())) { + if (o == proto) + return true; + } + return false; +} + +bool JSObject::propertyIsEnumerable(ExecState*, const Identifier& propertyName) const +{ + unsigned attributes; + + if (!getPropertyAttributes(propertyName, attributes)) + return false; + else + return !(attributes & DontEnum); +} + +bool JSObject::getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const +{ + if (_prop.get(propertyName, attributes)) + return true; + + // Look in the static hashtable of properties + const HashEntry* e = findPropertyHashEntry(propertyName); + if (e) { + attributes = e->attr; + return true; + } + + return false; +} + +void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + _prop.getEnumerablePropertyNames(propertyNames); + + // Add properties from the static hashtable of properties + const ClassInfo *info = classInfo(); + while (info) { + if (info->propHashTable) { + int size = info->propHashTable->size; + const HashEntry *e = info->propHashTable->entries; + for (int i = 0; i < size; ++i, ++e) { + if (e->s && !(e->attr & DontEnum)) + propertyNames.add(e->s); + } + } + info = info->parentClass; + } + if (_proto->isObject()) + static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames); +} + +bool JSObject::toBoolean(ExecState*) const +{ + return true; +} + +double JSObject::toNumber(ExecState *exec) const +{ + JSValue *prim = toPrimitive(exec,NumberType); + if (exec->hadException()) // should be picked up soon in nodes.cpp + return 0.0; + return prim->toNumber(exec); +} + +UString JSObject::toString(ExecState *exec) const +{ + JSValue *prim = toPrimitive(exec,StringType); + if (exec->hadException()) // should be picked up soon in nodes.cpp + return ""; + return prim->toString(exec); +} + +JSObject *JSObject::toObject(ExecState*) const +{ + return const_cast<JSObject*>(this); +} + +void JSObject::putDirect(const Identifier &propertyName, JSValue *value, int attr) +{ + _prop.put(propertyName, value, attr); +} + +void JSObject::putDirect(const Identifier &propertyName, int value, int attr) +{ + _prop.put(propertyName, jsNumber(value), attr); +} + +void JSObject::removeDirect(const Identifier &propertyName) +{ + _prop.remove(propertyName); +} + +void JSObject::putDirectFunction(InternalFunctionImp* func, int attr) +{ + putDirect(func->functionName(), func, attr); +} + +void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue **location) +{ + GetterSetterImp *gs = static_cast<GetterSetterImp *>(*location); + JSObject *getterFunc = gs->getGetter(); + if (getterFunc) + slot.setGetterSlot(this, getterFunc); + else + slot.setUndefined(this); +} + +// ------------------------------ Error ---------------------------------------- + +const char * const errorNamesArr[] = { + I18N_NOOP("Error"), // GeneralError + I18N_NOOP("Evaluation error"), // EvalError + I18N_NOOP("Range error"), // RangeError + I18N_NOOP("Reference error"), // ReferenceError + I18N_NOOP("Syntax error"), // SyntaxError + I18N_NOOP("Type error"), // TypeError + I18N_NOOP("URI error"), // URIError +}; + +const char * const * const Error::errorNames = errorNamesArr; + +JSObject *Error::create(ExecState *exec, ErrorType errtype, const UString &message, + int lineno, int sourceId, const UString &sourceURL) +{ + JSObject *cons; + switch (errtype) { + case EvalError: + cons = exec->lexicalGlobalObject()->evalErrorConstructor(); + break; + case RangeError: + cons = exec->lexicalGlobalObject()->rangeErrorConstructor(); + break; + case ReferenceError: + cons = exec->lexicalGlobalObject()->referenceErrorConstructor(); + break; + case SyntaxError: + cons = exec->lexicalGlobalObject()->syntaxErrorConstructor(); + break; + case TypeError: + cons = exec->lexicalGlobalObject()->typeErrorConstructor(); + break; + case URIError: + cons = exec->lexicalGlobalObject()->URIErrorConstructor(); + break; + default: + cons = exec->lexicalGlobalObject()->errorConstructor(); + break; + } + + List args; + if (message.isEmpty()) + args.append(jsString(errorNames[errtype])); + else + args.append(jsString(message)); + JSObject *err = static_cast<JSObject *>(cons->construct(exec,args)); + + if (lineno != -1) + err->put(exec, "line", jsNumber(lineno)); + if (sourceId != -1) + err->put(exec, "sourceId", jsNumber(sourceId)); + + if(!sourceURL.isNull()) + err->put(exec, "sourceURL", jsString(sourceURL)); + + return err; +} + +JSObject *Error::create(ExecState *exec, ErrorType type, const char *message) +{ + return create(exec, type, message, -1, -1, NULL); +} + +JSObject *throwError(ExecState *exec, ErrorType type) +{ + JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL); + exec->setException(error); + return error; +} + +JSObject *throwError(ExecState *exec, ErrorType type, const UString &message) +{ + JSObject *error = Error::create(exec, type, message, -1, -1, NULL); + exec->setException(error); + return error; +} + +JSObject *throwError(ExecState *exec, ErrorType type, const char *message) +{ + JSObject *error = Error::create(exec, type, message, -1, -1, NULL); + exec->setException(error); + return error; +} + +JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL) +{ + JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL); + exec->setException(error); + return error; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/object.h b/JavaScriptCore/kjs/object.h new file mode 100644 index 0000000..2d4e010 --- /dev/null +++ b/JavaScriptCore/kjs/object.h @@ -0,0 +1,586 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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. + * + */ + +#ifndef KJS_OBJECT_H +#define KJS_OBJECT_H + +#include "CommonIdentifiers.h" +#include "ExecState.h" +#include "JSType.h" +#include "list.h" +#include "property_map.h" +#include "property_slot.h" +#include "scope_chain.h" + +namespace KJS { + + class InternalFunctionImp; + class PropertyNameArray; + + struct HashEntry; + struct HashTable; + + // 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 + GetterSetter = 1 << 5 }; // property is a getter or setter + + /** + * Class Information + */ + 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. + */ + const HashTable* propHashTable; + }; + + // This is an internal value object which stores getter and setter functions + // for a property. + class GetterSetterImp : public JSCell { + public: + JSType type() const { return GetterSetterType; } + + GetterSetterImp() : getter(0), setter(0) { } + + virtual JSValue* toPrimitive(ExecState*, JSType preferred = UnspecifiedType) const; + virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value); + virtual bool toBoolean(ExecState *exec) const; + virtual double toNumber(ExecState *exec) const; + virtual UString toString(ExecState *exec) const; + virtual JSObject *toObject(ExecState *exec) const; + + virtual void mark(); + + JSObject *getGetter() { return getter; } + void setGetter(JSObject *g) { getter = g; } + JSObject *getSetter() { return setter; } + void setSetter(JSObject *s) { setter = s; } + + private: + JSObject *getter; + JSObject *setter; + }; + + class JSObject : public JSCell { + public: + /** + * Creates a new JSObject with the specified prototype + * + * @param proto The prototype + */ + JSObject(JSValue* proto); + + /** + * Creates a new JSObject with a prototype of jsNull() + * (that is, the ECMAScript "null" value, not a null object pointer). + */ + JSObject(); + + virtual void mark(); + virtual JSType type() const; + + /** + * A pointer to a ClassInfo struct for this class. This provides a basic + * facility for run-time type information, and can be used to check an + * object's class an inheritance (see inherits()). This should + * always return a statically declared pointer, or 0 to indicate that + * there is no class information. + * + * This is primarily useful if you have application-defined classes that you + * wish to check against for casting purposes. + * + * For example, to specify the class info for classes FooImp and BarImp, + * where FooImp inherits from BarImp, you would add the following in your + * class declarations: + * + * \code + * class BarImp : public JSObject { + * virtual const ClassInfo *classInfo() const { return &info; } + * static const ClassInfo info; + * // ... + * }; + * + * class FooImp : public JSObject { + * virtual const ClassInfo *classInfo() const { return &info; } + * static const ClassInfo info; + * // ... + * }; + * \endcode + * + * And in your source file: + * + * \code + * const ClassInfo BarImp::info = { "Bar", 0, 0 }; // no parent class + * const ClassInfo FooImp::info = { "Foo", &BarImp::info, 0 }; + * \endcode + * + * @see inherits() + */ + virtual const ClassInfo *classInfo() const; + + /** + * Checks whether this object inherits from the class with the specified + * classInfo() pointer. This requires that both this class and the other + * class return a non-NULL pointer for their classInfo() methods (otherwise + * it will return false). + * + * For example, for two JSObject pointers obj1 and obj2, you can check + * if obj1's class inherits from obj2's class using the following: + * + * if (obj1->inherits(obj2->classInfo())) { + * // ... + * } + * + * If you have a handle to a statically declared ClassInfo, such as in the + * classInfo() example, you can check for inheritance without needing + * an instance of the other class: + * + * if (obj1->inherits(FooImp::info)) { + * // ... + * } + * + * @param cinfo The ClassInfo pointer for the class you want to check + * inheritance against. + * @return true if this object's class inherits from class with the + * ClassInfo pointer specified in cinfo + */ + bool inherits(const ClassInfo *cinfo) const; + + // internal properties (ECMA 262-3 8.6.2) + + /** + * Returns the prototype of this object. Note that this is not the same as + * the "prototype" property. + * + * See ECMA 8.6.2 + * + * @return The object's prototype + */ + JSValue *prototype() const; + void setPrototype(JSValue *proto); + + /** + * Returns the class name of the object + * + * See ECMA 8.6.2 + * + * @return The object's class name + */ + /** + * Implementation of the [[Class]] internal property (implemented by all + * Objects) + * + * The default implementation uses classInfo(). + * You should either implement classInfo(), or + * if you simply need a classname, you can reimplement className() + * instead. + */ + virtual UString className() const; + + /** + * Retrieves the specified property from the object. If neither the object + * or any other object in it's prototype chain have the property, this + * function will return Undefined. + * + * See ECMA 8.6.2.1 + * + * @param exec The current execution state + * @param propertyName The name of the property to retrieve + * + * @return The specified property, or Undefined + */ + JSValue *get(ExecState *exec, const Identifier &propertyName) const; + JSValue *get(ExecState *exec, unsigned propertyName) const; + + bool getPropertySlot(ExecState *, const Identifier&, PropertySlot&); + bool getPropertySlot(ExecState *, unsigned, PropertySlot&); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState *, unsigned index, PropertySlot&); + + /** + * Sets the specified property. + * + * See ECMA 8.6.2.2 + * + * @param exec The current execution state + * @param propertyName The name of the property to set + * @param propertyValue The value to set + */ + virtual void put(ExecState*, const Identifier& propertyName, JSValue* value); + virtual void put(ExecState*, unsigned propertyName, JSValue* value); + + /** + * Checks if a property is enumerable, that is if it doesn't have the DontEnum + * flag set + * + * See ECMA 15.2.4 + * @param exec The current execution state + * @param propertyName The name of the property + * @return true if the property is enumerable, otherwise false + */ + bool propertyIsEnumerable(ExecState *exec, const Identifier &propertyName) const; + + /** + * Checks to see whether the object (or any object in it's prototype chain) + * has a property with the specified name. + * + * See ECMA 8.6.2.4 + * + * @param exec The current execution state + * @param propertyName The name of the property to check for + * @return true if the object has the property, otherwise false + */ + bool hasProperty(ExecState*, const Identifier&) const; + bool hasProperty(ExecState*, unsigned) const; + bool hasOwnProperty(ExecState*, const Identifier&) const; + + /** + * Removes the specified property from the object. + * + * See ECMA 8.6.2.5 + * + * @param exec The current execution state + * @param propertyName The name of the property to delete + * @return true if the property was successfully deleted or did not + * exist on the object. false if deleting the specified property is not + * allowed. + */ + virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName); + virtual bool deleteProperty(ExecState *exec, unsigned propertyName); + + /** + * Converts the object into a primitive value. The value return may differ + * depending on the supplied hint + * + * See ECMA 8.6.2.6 + * + * @param exec The current execution state + * @param hint The desired primitive type to convert to + * @return A primitive value converted from the objetc. Note that the + * type of primitive value returned may not be the same as the requested + * hint. + */ + /** + * Implementation of the [[DefaultValue]] internal property (implemented by + * all Objects) + */ + virtual JSValue *defaultValue(ExecState *exec, JSType hint) const; + + /** + * Whether or not the object implements the construct() method. If this + * returns false you should not call the construct() method on this + * object (typically, an assertion will fail to indicate this). + * + * @return true if this object implements the construct() method, otherwise + * false + */ + virtual bool implementsConstruct() const; + + /** + * Creates a new object based on this object. Typically this means the + * following: + * 1. A new object is created + * 2. The prototype of the new object is set to the value of this object's + * "prototype" property + * 3. The call() method of this object is called, with the new object + * passed as the this value + * 4. The new object is returned + * + * In some cases, Host objects may differ from these semantics, although + * this is discouraged. + * + * If an error occurs during construction, the execution state's exception + * will be set. This can be tested for with ExecState::hadException(). + * Under some circumstances, the exception object may also be returned. + * + * Note: This function should not be called if implementsConstruct() returns + * false, in which case it will result in an assertion failure. + * + * @param exec The current execution state + * @param args The arguments to be passed to call() once the new object has + * been created + * @return The newly created & initialized object + */ + /** + * Implementation of the [[Construct]] internal property + */ + virtual JSObject* construct(ExecState* exec, const List& args); + virtual JSObject* construct(ExecState* exec, const List& args, const Identifier& functionName, const UString& sourceURL, int lineNumber); + + /** + * Whether or not the object implements the call() method. If this returns + * false you should not call the call() method on this object (typically, + * an assertion will fail to indicate this). + * + * @return true if this object implements the call() method, otherwise + * false + */ + virtual bool implementsCall() const; + + /** + * Calls this object as if it is a function. + * + * Note: This function should not be called if implementsCall() returns + * false, in which case it will result in an assertion failure. + * + * See ECMA 8.6.2.3 + * + * @param exec The current execution state + * @param thisObj The obj to be used as "this" within function execution. + * Note that in most cases this will be different from the C++ "this" + * object. For example, if the ECMAScript code "window.location->toString()" + * is executed, call() will be invoked on the C++ object which implements + * the toString method, with the thisObj being window.location + * @param args List of arguments to be passed to the function + * @return The return value from the function + */ + JSValue *call(ExecState *exec, JSObject *thisObj, const List &args); + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + + /** + * Whether or not the object implements the hasInstance() method. If this + * returns false you should not call the hasInstance() method on this + * object (typically, an assertion will fail to indicate this). + * + * @return true if this object implements the hasInstance() method, + * otherwise false + */ + virtual bool implementsHasInstance() const; + + /** + * Checks whether value delegates behavior to this object. Used by the + * instanceof operator. + * + * @param exec The current execution state + * @param value The value to check + * @return true if value delegates behavior to this object, otherwise + * false + */ + virtual bool hasInstance(ExecState *exec, JSValue *value); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual JSValue* toPrimitive(ExecState*, JSType preferredType = UnspecifiedType) const; + virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value); + virtual bool toBoolean(ExecState *exec) const; + virtual double toNumber(ExecState *exec) const; + virtual UString toString(ExecState *exec) const; + virtual JSObject *toObject(ExecState *exec) const; + + bool getPropertyAttributes(const Identifier& propertyName, unsigned& attributes) const; + + // WebCore uses this to make document.all and style.filter undetectable + virtual bool masqueradeAsUndefined() const { return false; } + + // This get function only looks at the property map. + // This is used e.g. by lookupOrCreateFunction (to cache a function, we don't want + // to look up in the prototype, it might already exist there) + JSValue *getDirect(const Identifier& propertyName) const + { return _prop.get(propertyName); } + JSValue **getDirectLocation(const Identifier& propertyName) + { return _prop.getLocation(propertyName); } + void putDirect(const Identifier &propertyName, JSValue *value, int attr = 0); + void putDirect(const Identifier &propertyName, int value, int attr = 0); + void removeDirect(const Identifier &propertyName); + + // convenience to add a function property under the function's own built-in name + void putDirectFunction(InternalFunctionImp*, int attr = 0); + + void fillGetterPropertySlot(PropertySlot& slot, JSValue **location); + + void defineGetter(ExecState *exec, const Identifier& propertyName, JSObject *getterFunc); + void defineSetter(ExecState *exec, const Identifier& propertyName, JSObject *setterFunc); + + void saveProperties(SavedProperties &p) const { _prop.save(p); } + void restoreProperties(const SavedProperties &p) { _prop.restore(p); } + + virtual bool isActivationObject() const { return false; } + virtual bool isGlobalObject() const { return false; } + + protected: + PropertyMap _prop; + + private: + const HashEntry* findPropertyHashEntry( const Identifier& propertyName ) const; + JSValue *_proto; + }; + + /** + * Types of Native Errors available. For custom errors, GeneralError + * should be used. + */ + enum ErrorType { GeneralError = 0, + EvalError = 1, + RangeError = 2, + ReferenceError = 3, + SyntaxError = 4, + TypeError = 5, + URIError = 6}; + + /** + * @short Factory methods for error objects. + */ + class Error { + public: + /** + * Factory method for error objects. + * + * @param exec The current execution state + * @param errtype Type of error. + * @param message Optional error message. + * @param lineNumber Optional line number. + * @param sourceId Optional source id. + * @param sourceURL Optional source URL. + */ + static JSObject *create(ExecState *, ErrorType, const UString &message, int lineNumber, int sourceId, const UString &sourceURL); + static JSObject *create(ExecState *, ErrorType, const char *message); + + /** + * Array of error names corresponding to ErrorType + */ + static const char * const * const errorNames; + }; + +JSObject *throwError(ExecState *, ErrorType, const UString &message, int lineNumber, int sourceId, const UString &sourceURL); +JSObject *throwError(ExecState *, ErrorType, const UString &message); +JSObject *throwError(ExecState *, ErrorType, const char *message); +JSObject *throwError(ExecState *, ErrorType); + +inline JSObject::JSObject(JSValue* proto) + : _proto(proto) +{ + ASSERT(proto); +} + +inline JSObject::JSObject() + : _proto(jsNull()) +{ +} + +inline JSValue *JSObject::prototype() const +{ + return _proto; +} + +inline void JSObject::setPrototype(JSValue *proto) +{ + ASSERT(proto); + _proto = proto; +} + +inline bool JSObject::inherits(const ClassInfo *info) const +{ + for (const ClassInfo *ci = classInfo(); ci; ci = ci->parentClass) + if (ci == info) + return true; + return false; +} + +// this method is here to be after the inline declaration of JSObject::inherits +inline bool JSCell::isObject(const ClassInfo *info) const +{ + return isObject() && static_cast<const JSObject *>(this)->inherits(info); +} + +// this method is here to be after the inline declaration of JSCell::isObject +inline bool JSValue::isObject(const ClassInfo *c) const +{ + return !JSImmediate::isImmediate(this) && asCell()->isObject(c); +} + +// 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 +inline bool JSObject::getPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) +{ + JSObject *object = this; + while (true) { + if (object->getOwnPropertySlot(exec, propertyName, slot)) + return true; + + JSValue *proto = object->_proto; + if (!proto->isObject()) + return false; + + object = static_cast<JSObject *>(proto); + } +} + +// It may seem crazy to inline a function this large, especially a virtual function, +// but it makes a big difference to property lookup that derived classes can inline their +// base class call to this. +ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (JSValue **location = getDirectLocation(propertyName)) { + if (_prop.hasGetterSetterProperties() && location[0]->type() == GetterSetterType) + fillGetterPropertySlot(slot, location); + else + slot.setValueSlot(this, location); + return true; + } + + // non-standard Netscape extension + if (propertyName == exec->propertyNames().underscoreProto) { + slot.setValueSlot(this, &_proto); + return true; + } + + return false; +} + +inline void ScopeChain::release() +{ + // This function is only called by deref(), + // Deref ensures these conditions are true. + ASSERT(_node && _node->refCount == 0); + ScopeChainNode *n = _node; + do { + ScopeChainNode *next = n->next; + delete n; + n = next; + } while (n && --n->refCount == 0); +} + +inline JSValue* JSObject::toPrimitive(ExecState* exec, JSType preferredType) const +{ + return defaultValue(exec, preferredType); +} + +} // namespace + +#endif // KJS_OBJECT_H diff --git a/JavaScriptCore/kjs/object_object.cpp b/JavaScriptCore/kjs/object_object.cpp new file mode 100644 index 0000000..90eaa93 --- /dev/null +++ b/JavaScriptCore/kjs/object_object.cpp @@ -0,0 +1,216 @@ +/* + * 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 "object_object.h" + +#include "JSGlobalObject.h" +#include "operations.h" +#include "function_object.h" +#include <stdio.h> + +namespace KJS { + +// ------------------------------ ObjectPrototype -------------------------------- + +static JSValue* objectProtoFuncValueOf(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncHasOwnProperty(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncIsPrototypeOf(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncDefineGetter(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncDefineSetter(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncLookupGetter(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncLookupSetter(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncPropertyIsEnumerable(ExecState*, JSObject*, const List&); +static JSValue* objectProtoFuncToLocaleString(ExecState*, JSObject*, const List&); + +ObjectPrototype::ObjectPrototype(ExecState* exec, FunctionPrototype* functionPrototype) + : JSObject() // [[Prototype]] is null +{ + static const Identifier* hasOwnPropertyPropertyName = new Identifier("hasOwnProperty"); + static const Identifier* propertyIsEnumerablePropertyName = new Identifier("propertyIsEnumerable"); + static const Identifier* isPrototypeOfPropertyName = new Identifier("isPrototypeOf"); + static const Identifier* defineGetterPropertyName = new Identifier("__defineGetter__"); + static const Identifier* defineSetterPropertyName = new Identifier("__defineSetter__"); + static const Identifier* lookupGetterPropertyName = new Identifier("__lookupGetter__"); + static const Identifier* lookupSetterPropertyName = new Identifier("__lookupSetter__"); + + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().valueOf, objectProtoFuncValueOf), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, *hasOwnPropertyPropertyName, objectProtoFuncHasOwnProperty), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, *propertyIsEnumerablePropertyName, objectProtoFuncPropertyIsEnumerable), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, *isPrototypeOfPropertyName, objectProtoFuncIsPrototypeOf), DontEnum); + + // Mozilla extensions + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 2, *defineGetterPropertyName, objectProtoFuncDefineGetter), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 2, *defineSetterPropertyName, objectProtoFuncDefineSetter), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, *lookupGetterPropertyName, objectProtoFuncLookupGetter), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 1, *lookupSetterPropertyName, objectProtoFuncLookupSetter), DontEnum); +} + + +// ------------------------------ Functions -------------------------------- + +// ECMA 15.2.4.2, 15.2.4.4, 15.2.4.5, 15.2.4.7 + +JSValue* objectProtoFuncValueOf(ExecState*, JSObject* thisObj, const List&) +{ + return thisObj; +} + +JSValue* objectProtoFuncHasOwnProperty(ExecState* exec, JSObject* thisObj, const List& args) +{ + return jsBoolean(thisObj->hasOwnProperty(exec, Identifier(args[0]->toString(exec)))); +} + +JSValue* objectProtoFuncIsPrototypeOf(ExecState*, JSObject* thisObj, const List& args) +{ + if (!args[0]->isObject()) + return jsBoolean(false); + + JSValue* v = static_cast<JSObject*>(args[0])->prototype(); + + while (true) { + if (!v->isObject()) + return jsBoolean(false); + if (thisObj == static_cast<JSObject*>(v)) + return jsBoolean(true); + v = static_cast<JSObject*>(v)->prototype(); + } +} + +JSValue* objectProtoFuncDefineGetter(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!args[1]->isObject() || !static_cast<JSObject*>(args[1])->implementsCall()) + return throwError(exec, SyntaxError, "invalid getter usage"); + + thisObj->defineGetter(exec, Identifier(args[0]->toString(exec)), static_cast<JSObject *>(args[1])); + return jsUndefined(); +} + +JSValue* objectProtoFuncDefineSetter(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!args[1]->isObject() || !static_cast<JSObject*>(args[1])->implementsCall()) + return throwError(exec, SyntaxError, "invalid setter usage"); + + thisObj->defineSetter(exec, Identifier(args[0]->toString(exec)), static_cast<JSObject *>(args[1])); + return jsUndefined(); +} + +JSValue* objectProtoFuncLookupGetter(ExecState* exec, JSObject* thisObj, const List& args) +{ + Identifier propertyName = Identifier(args[0]->toString(exec)); + JSObject* obj = thisObj; + while (true) { + JSValue* v = obj->getDirect(propertyName); + if (v) { + if (v->type() != GetterSetterType) + return jsUndefined(); + JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getGetter(); + if (!funcObj) + return jsUndefined(); + return funcObj; + } + + if (!obj->prototype() || !obj->prototype()->isObject()) + return jsUndefined(); + obj = static_cast<JSObject*>(obj->prototype()); + } +} + +JSValue* objectProtoFuncLookupSetter(ExecState* exec, JSObject* thisObj, const List& args) +{ + Identifier propertyName = Identifier(args[0]->toString(exec)); + JSObject* obj = thisObj; + while (true) { + JSValue* v = obj->getDirect(propertyName); + if (v) { + if (v->type() != GetterSetterType) + return jsUndefined(); + JSObject* funcObj = static_cast<GetterSetterImp*>(v)->getSetter(); + if (!funcObj) + return jsUndefined(); + return funcObj; + } + + if (!obj->prototype() || !obj->prototype()->isObject()) + return jsUndefined(); + obj = static_cast<JSObject*>(obj->prototype()); + } +} + +JSValue* objectProtoFuncPropertyIsEnumerable(ExecState* exec, JSObject* thisObj, const List& args) +{ + return jsBoolean(thisObj->propertyIsEnumerable(exec, Identifier(args[0]->toString(exec)))); +} + +JSValue* objectProtoFuncToLocaleString(ExecState* exec, JSObject* thisObj, const List&) +{ + return jsString(thisObj->toString(exec)); +} + +JSValue* objectProtoFuncToString(ExecState*, JSObject* thisObj, const List&) +{ + return jsString("[object " + thisObj->className() + "]"); +} + +// ------------------------------ ObjectObjectImp -------------------------------- + +ObjectObjectImp::ObjectObjectImp(ExecState* exec, ObjectPrototype* objProto, FunctionPrototype* funcProto) + : InternalFunctionImp(funcProto, "Object") +{ + // ECMA 15.2.3.1 + putDirect(exec->propertyNames().prototype, objProto, DontEnum|DontDelete|ReadOnly); + + // no. of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); +} + + +bool ObjectObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.2.2 +JSObject* ObjectObjectImp::construct(ExecState* exec, const List& args) +{ + JSValue* arg = args[0]; + switch (arg->type()) { + case StringType: + case BooleanType: + case NumberType: + case ObjectType: + return arg->toObject(exec); + case NullType: + case UndefinedType: + return new JSObject(exec->lexicalGlobalObject()->objectPrototype()); + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +JSValue* ObjectObjectImp::callAsFunction(ExecState* exec, JSObject* /*thisObj*/, const List &args) +{ + return construct(exec, args); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/object_object.h b/JavaScriptCore/kjs/object_object.h new file mode 100644 index 0000000..0ee5482 --- /dev/null +++ b/JavaScriptCore/kjs/object_object.h @@ -0,0 +1,57 @@ +/* + * 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 _OBJECT_OBJECT_H_ +#define _OBJECT_OBJECT_H_ + +#include "function.h" + +namespace KJS { + + /** + * @internal + * + * The initial value of Object.prototype (and thus all objects created + * with the Object constructor + */ + class ObjectPrototype : public JSObject { + public: + ObjectPrototype(ExecState*, FunctionPrototype*); + }; + + JSValue* objectProtoFuncToString(ExecState*, JSObject*, const List&); + + /** + * @internal + * + * The initial value of the the global variable's "Object" property + */ + class ObjectObjectImp : public InternalFunctionImp { + public: + ObjectObjectImp(ExecState*, ObjectPrototype*, FunctionPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + }; + +} // namespace KJS + +#endif // _OBJECT_OBJECT_H_ diff --git a/JavaScriptCore/kjs/operations.cpp b/JavaScriptCore/kjs/operations.cpp new file mode 100644 index 0000000..d2b3892 --- /dev/null +++ b/JavaScriptCore/kjs/operations.cpp @@ -0,0 +1,128 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.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 "operations.h" + +#include "internal.h" +#include "object.h" +#include <math.h> +#include <stdio.h> +#include <wtf/MathExtras.h> + +#if HAVE(FLOAT_H) +#include <float.h> +#endif + +namespace KJS { + +// ECMA 11.9.3 +bool equal(ExecState *exec, JSValue *v1, JSValue *v2) +{ + JSType t1 = v1->type(); + JSType t2 = v2->type(); + + if (t1 != t2) { + if (t1 == UndefinedType) + t1 = NullType; + if (t2 == UndefinedType) + t2 = NullType; + + if (t1 == BooleanType) + t1 = NumberType; + if (t2 == BooleanType) + t2 = NumberType; + + if (t1 == NumberType && t2 == StringType) { + // use toNumber + } else if (t1 == StringType && t2 == NumberType) + t1 = NumberType; + // use toNumber + else { + if ((t1 == StringType || t1 == NumberType) && t2 == ObjectType) { + v2 = v2->toPrimitive(exec); + if (exec->hadException()) + return false; + return equal(exec, v1, v2); + } + if (t1 == NullType && t2 == ObjectType) + return static_cast<JSObject *>(v2)->masqueradeAsUndefined(); + if (t1 == ObjectType && (t2 == StringType || t2 == NumberType)) { + v1 = v1->toPrimitive(exec); + if (exec->hadException()) + return false; + return equal(exec, v1, v2); + } + if (t1 == ObjectType && t2 == NullType) + return static_cast<JSObject *>(v1)->masqueradeAsUndefined(); + if (t1 != t2) + return false; + } + } + + if (t1 == UndefinedType || t1 == NullType) + return true; + + if (t1 == NumberType) { + double d1 = v1->toNumber(exec); + double d2 = v2->toNumber(exec); + return d1 == d2; + } + + if (t1 == StringType) + return static_cast<StringImp*>(v1)->value() == static_cast<StringImp*>(v2)->value(); + + if (t1 == BooleanType) + return v1->toBoolean(exec) == v2->toBoolean(exec); + + // types are Object + return v1 == v2; +} + +bool strictEqual(ExecState *exec, JSValue *v1, JSValue *v2) +{ + JSType t1 = v1->type(); + JSType t2 = v2->type(); + + if (t1 != t2) + return false; + if (t1 == UndefinedType || t1 == NullType) + return true; + if (t1 == NumberType) { + double n1 = v1->toNumber(exec); + double n2 = v2->toNumber(exec); + if (n1 == n2) + return true; + return false; + } else if (t1 == StringType) + return v1->toString(exec) == v2->toString(exec); + else if (t2 == BooleanType) + return v1->toBoolean(exec) == v2->toBoolean(exec); + + if (v1 == v2) + return true; + /* TODO: joined objects */ + + return false; +} + +} diff --git a/JavaScriptCore/kjs/operations.h b/JavaScriptCore/kjs/operations.h new file mode 100644 index 0000000..96656f0 --- /dev/null +++ b/JavaScriptCore/kjs/operations.h @@ -0,0 +1,35 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef _KJS_OPERATIONS_H_ +#define _KJS_OPERATIONS_H_ + +namespace KJS { + + class ExecState; + class JSValue; + + bool equal(ExecState *exec, JSValue *v1, JSValue *v2); + bool strictEqual(ExecState *exec, JSValue *v1, JSValue *v2); +} + +#endif diff --git a/JavaScriptCore/kjs/property_map.cpp b/JavaScriptCore/kjs/property_map.cpp new file mode 100644 index 0000000..1da4917 --- /dev/null +++ b/JavaScriptCore/kjs/property_map.cpp @@ -0,0 +1,850 @@ +/* + * 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. + * + */ + +#include "config.h" +#include "property_map.h" + +#include "object.h" +#include "protect.h" +#include "PropertyNameArray.h" +#include <algorithm> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/HashTable.h> +#include <wtf/Vector.h> + +using std::max; +using WTF::doubleHash; + +#ifndef NDEBUG +#define DO_PROPERTYMAP_CONSTENCY_CHECK 0 +#define DUMP_PROPERTYMAP_STATS 0 +#else +#define DO_PROPERTYMAP_CONSTENCY_CHECK 0 +#define DUMP_PROPERTYMAP_STATS 0 +#endif + +#define USE_SINGLE_ENTRY 1 + +// 2/28/2006 ggaren: command-line JS iBench says that USE_SINGLE_ENTRY is a +// 3.2% performance boost. + +namespace KJS { + +// Choose a number for the following so that most property maps are smaller, +// but it's not going to blow out the stack to allocate this number of pointers. +static const int smallMapThreshold = 1024; + +// The point at which the function call overhead of the qsort implementation +// becomes small compared to the inefficiency of insertion sort. +static const unsigned tinyMapThreshold = 20; + +#if DUMP_PROPERTYMAP_STATS + +static int numProbes; +static int numCollisions; +static int numRehashes; +static int numRemoves; + +struct PropertyMapStatisticsExitLogger { ~PropertyMapStatisticsExitLogger(); }; + +static PropertyMapStatisticsExitLogger logger; + +PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() +{ + printf("\nKJS::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 + +struct PropertyMapEntry { + UString::Rep* key; + JSValue* value; + unsigned attributes; + unsigned index; + + PropertyMapEntry(UString::Rep* k, JSValue* v, int a) + : key(k), value(v), attributes(a), index(0) + { + } +}; + +// lastIndexUsed is an ever-increasing index used to identify the order items +// were inserted into the property map. It's required that getEnumerablePropertyNames +// return the properties in the order they were added for compatibility with other +// browsers' JavaScript implementations. +struct PropertyMapHashTable { + unsigned sizeMask; + unsigned size; + unsigned keyCount; + unsigned deletedSentinelCount; + unsigned lastIndexUsed; + unsigned entryIndicies[1]; + + PropertyMapEntry* entries() + { + // The entries vector comes after the indices vector. + // The 0th item in the entries vector is not really used; it has to + // have a 0 in its key to allow the hash table lookup to handle deleted + // sentinels without any special-case code, but the other fields are unused. + return reinterpret_cast<PropertyMapEntry*>(&entryIndicies[size]); + } + + static size_t allocationSize(unsigned size) + { + // We never let a hash table get more than half full, + // So the number of indices we need is the size of the hash table. + // But the number of entries is half that (plus one for the deleted sentinel). + return sizeof(PropertyMapHashTable) + + (size - 1) * sizeof(unsigned) + + (1 + size / 2) * sizeof(PropertyMapEntry); + } +}; + +static const unsigned emptyEntryIndex = 0; +static const unsigned deletedSentinelIndex = 1; + +SavedProperties::SavedProperties() + : count(0) +{ +} + +SavedProperties::~SavedProperties() +{ +} + +#if !DO_PROPERTYMAP_CONSTENCY_CHECK + +inline void PropertyMap::checkConsistency() +{ +} + +#endif + +PropertyMap::~PropertyMap() +{ + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (m_singleEntryKey) + m_singleEntryKey->deref(); +#endif + return; + } + + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (UString::Rep* key = m_u.table->entries()[i].key) + key->deref(); + } + fastFree(m_u.table); +} + +void PropertyMap::clear() +{ + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (m_singleEntryKey) { + m_singleEntryKey->deref(); + m_singleEntryKey = 0; + } +#endif + return; + } + + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (UString::Rep* key = m_u.table->entries()[i].key) + key->deref(); + } + for (unsigned i = 0; i < m_u.table->size; i++) + m_u.table->entryIndicies[i] = emptyEntryIndex; + m_u.table->keyCount = 0; + m_u.table->deletedSentinelCount = 0; +} + +JSValue* PropertyMap::get(const Identifier& name, unsigned& attributes) const +{ + ASSERT(!name.isNull()); + + UString::Rep* rep = name._ustring.rep(); + + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (rep == m_singleEntryKey) { + attributes = m_singleEntryAttributes; + return m_u.singleEntryValue; + } +#endif + return 0; + } + + unsigned i = rep->computedHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return 0; + + if (rep == m_u.table->entries()[entryIndex - 1].key) { + attributes = m_u.table->entries()[entryIndex - 1].attributes; + return m_u.table->entries()[entryIndex - 1].value; + } + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->computedHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return 0; + + if (rep == m_u.table->entries()[entryIndex - 1].key) { + attributes = m_u.table->entries()[entryIndex - 1].attributes; + return m_u.table->entries()[entryIndex - 1].value; + } + } +} + +JSValue* PropertyMap::get(const Identifier& name) const +{ + ASSERT(!name.isNull()); + + UString::Rep* rep = name._ustring.rep(); + + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (rep == m_singleEntryKey) + return m_u.singleEntryValue; +#endif + return 0; + } + + unsigned i = rep->computedHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return 0; + + if (rep == m_u.table->entries()[entryIndex - 1].key) + return m_u.table->entries()[entryIndex - 1].value; + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->computedHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return 0; + + if (rep == m_u.table->entries()[entryIndex - 1].key) + return m_u.table->entries()[entryIndex - 1].value; + } +} + +JSValue** PropertyMap::getLocation(const Identifier& name) +{ + ASSERT(!name.isNull()); + + UString::Rep* rep = name._ustring.rep(); + + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (rep == m_singleEntryKey) + return &m_u.singleEntryValue; +#endif + return 0; + } + + unsigned i = rep->computedHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return 0; + + if (rep == m_u.table->entries()[entryIndex - 1].key) + return &m_u.table->entries()[entryIndex - 1].value; + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->computedHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return 0; + + if (rep == m_u.table->entries()[entryIndex - 1].key) + return &m_u.table->entries()[entryIndex - 1].value; + } +} + +void PropertyMap::put(const Identifier& name, JSValue* value, unsigned attributes, bool checkReadOnly) +{ + ASSERT(!name.isNull()); + ASSERT(value); + + checkConsistency(); + + UString::Rep* rep = name._ustring.rep(); + +#if USE_SINGLE_ENTRY + if (!m_usingTable) { + if (!m_singleEntryKey) { + rep->ref(); + m_singleEntryKey = rep; + m_u.singleEntryValue = value; + m_singleEntryAttributes = static_cast<short>(attributes); + checkConsistency(); + return; + } + if (rep == m_singleEntryKey && !(checkReadOnly && (m_singleEntryAttributes & ReadOnly))) { + m_u.singleEntryValue = value; + return; + } + } +#endif + + if (!m_usingTable || (m_u.table->keyCount + m_u.table->deletedSentinelCount) * 2 >= m_u.table->size) + expand(); + + // FIXME: Consider a fast case for tables with no deleted sentinels. + + unsigned i = rep->computedHash(); + unsigned k = 0; + bool foundDeletedElement = false; + unsigned deletedElementIndex = 0; // initialize to make the compiler happy + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + while (1) { + unsigned entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + break; + + if (m_u.table->entries()[entryIndex - 1].key == rep) { + if (checkReadOnly && (m_u.table->entries()[entryIndex - 1].attributes & ReadOnly)) + return; + // Put a new value in an existing hash table entry. + m_u.table->entries()[entryIndex - 1].value = value; + // Attributes are intentionally not updated. + return; + } else if (entryIndex == deletedSentinelIndex) { + // If we find a deleted-element sentinel, remember it for use later. + if (!foundDeletedElement) { + foundDeletedElement = true; + deletedElementIndex = i; + } + } + + if (k == 0) { + k = 1 | doubleHash(rep->computedHash()); +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + } + + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } + + // Figure out which entry to use. + unsigned entryIndex = m_u.table->keyCount + m_u.table->deletedSentinelCount + 2; + if (foundDeletedElement) { + i = deletedElementIndex; + --m_u.table->deletedSentinelCount; + + // Since we're not making the table bigger, we can't use the entry one past + // the end that we were planning on using, so search backwards for the empty + // slot that we can use. We know it will be there because we did at least one + // deletion in the past that left an entry empty. + while (m_u.table->entries()[--entryIndex - 1].key) + ; + } + + + // Create a new hash table entry. + m_u.table->entryIndicies[i & m_u.table->sizeMask] = entryIndex; + + // Create a new hash table entry. + rep->ref(); + m_u.table->entries()[entryIndex - 1].key = rep; + m_u.table->entries()[entryIndex - 1].value = value; + m_u.table->entries()[entryIndex - 1].attributes = attributes; + m_u.table->entries()[entryIndex - 1].index = ++m_u.table->lastIndexUsed; + ++m_u.table->keyCount; + + checkConsistency(); +} + +void PropertyMap::insert(const Entry& entry) +{ + ASSERT(m_u.table); + + unsigned i = entry.key->computedHash(); + unsigned k = 0; + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + while (1) { + unsigned entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + break; + + if (k == 0) { + k = 1 | doubleHash(entry.key->computedHash()); +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + } + + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } + + unsigned entryIndex = m_u.table->keyCount + 2; + m_u.table->entryIndicies[i & m_u.table->sizeMask] = entryIndex; + m_u.table->entries()[entryIndex - 1] = entry; + ++m_u.table->keyCount; +} + +void PropertyMap::expand() +{ + if (!m_usingTable) + createTable(); + else + rehash(m_u.table->size * 2); +} + +void PropertyMap::rehash() +{ + ASSERT(m_usingTable); + ASSERT(m_u.table); + ASSERT(m_u.table->size); + rehash(m_u.table->size); +} + +void PropertyMap::createTable() +{ + const unsigned newTableSize = 16; + + ASSERT(!m_usingTable); + + checkConsistency(); + +#if USE_SINGLE_ENTRY + JSValue* oldSingleEntryValue = m_u.singleEntryValue; +#endif + + m_u.table = static_cast<Table*>(fastZeroedMalloc(Table::allocationSize(newTableSize))); + m_u.table->size = newTableSize; + m_u.table->sizeMask = newTableSize - 1; + m_usingTable = true; + +#if USE_SINGLE_ENTRY + if (m_singleEntryKey) { + insert(Entry(m_singleEntryKey, oldSingleEntryValue, m_singleEntryAttributes)); + m_singleEntryKey = 0; + } +#endif + + checkConsistency(); +} + +void PropertyMap::rehash(unsigned newTableSize) +{ + ASSERT(!m_singleEntryKey); + ASSERT(m_u.table); + ASSERT(m_usingTable); + + checkConsistency(); + + Table* oldTable = m_u.table; + + m_u.table = static_cast<Table*>(fastZeroedMalloc(Table::allocationSize(newTableSize))); + m_u.table->size = newTableSize; + m_u.table->sizeMask = newTableSize - 1; + + unsigned lastIndexUsed = 0; + unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; ++i) { + if (oldTable->entries()[i].key) { + lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed); + insert(oldTable->entries()[i]); + } + } + m_u.table->lastIndexUsed = lastIndexUsed; + + fastFree(oldTable); + + checkConsistency(); +} + +void PropertyMap::remove(const Identifier& name) +{ + ASSERT(!name.isNull()); + + checkConsistency(); + + UString::Rep* rep = name._ustring.rep(); + + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (rep == m_singleEntryKey) { + m_singleEntryKey->deref(); + m_singleEntryKey = 0; + checkConsistency(); + } +#endif + return; + } + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; + ++numRemoves; +#endif + + // Find the thing to remove. + unsigned i = rep->computedHash(); + unsigned k = 0; + unsigned entryIndex; + UString::Rep* key = 0; + while (1) { + entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + if (entryIndex == emptyEntryIndex) + return; + + key = m_u.table->entries()[entryIndex - 1].key; + if (rep == key) + break; + + if (k == 0) { + k = 1 | doubleHash(rep->computedHash()); +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + } + + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + } + + // Replace this one element with the deleted sentinel. Also clear out + // the entry so we can iterate all the entries as needed. + m_u.table->entryIndicies[i & m_u.table->sizeMask] = deletedSentinelIndex; + key->deref(); + m_u.table->entries()[entryIndex - 1].key = 0; + m_u.table->entries()[entryIndex - 1].value = jsUndefined(); + m_u.table->entries()[entryIndex - 1].attributes = 0; + ASSERT(m_u.table->keyCount >= 1); + --m_u.table->keyCount; + ++m_u.table->deletedSentinelCount; + + if (m_u.table->deletedSentinelCount * 4 >= m_u.table->size) + rehash(); + + checkConsistency(); +} + +void PropertyMap::mark() const +{ + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (m_singleEntryKey) { + JSValue* v = m_u.singleEntryValue; + if (!v->marked()) + v->mark(); + } +#endif + return; + } + + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + JSValue* v = m_u.table->entries()[i].value; + if (!v->marked()) + v->mark(); + } +} + +static int comparePropertyMapEntryIndices(const void* a, const void* b) +{ + unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index; + unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index; + if (ia < ib) + return -1; + if (ia > ib) + return +1; + return 0; +} + +bool PropertyMap::containsGettersOrSetters() const +{ + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + return !!(m_singleEntryAttributes & GetterSetter); +#else + return false; +#endif + } + + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_u.table->entries()[i].attributes & GetterSetter) + return true; + } + + return false; +} + +void PropertyMap::getEnumerablePropertyNames(PropertyNameArray& propertyNames) const +{ + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + UString::Rep* key = m_singleEntryKey; + if (key && !(m_singleEntryAttributes & DontEnum)) + propertyNames.add(Identifier(key)); +#endif + return; + } + + if (m_u.table->keyCount < tinyMapThreshold) { + Entry* a[tinyMapThreshold]; + int i = 0; + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned k = 1; k <= entryCount; k++) { + if (m_u.table->entries()[k].key && !(m_u.table->entries()[k].attributes & DontEnum)) { + Entry* value = &m_u.table->entries()[k]; + int j; + for (j = i - 1; j >= 0 && a[j]->index > value->index; --j) + a[j + 1] = a[j]; + a[j + 1] = value; + ++i; + } + } + for (int k = 0; k < i; ++k) + propertyNames.add(Identifier(a[k]->key)); + return; + } + + // Allocate a buffer to use to sort the keys. + Vector<Entry*, smallMapThreshold> sortedEnumerables(m_u.table->keyCount); + + // Get pointers to the enumerable entries in the buffer. + Entry** p = sortedEnumerables.data(); + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_u.table->entries()[i].key && !(m_u.table->entries()[i].attributes & DontEnum)) + *p++ = &m_u.table->entries()[i]; + } + + // Sort the entries by index. + qsort(sortedEnumerables.data(), p - sortedEnumerables.data(), sizeof(Entry*), comparePropertyMapEntryIndices); + + // Put the keys of the sorted entries into the list. + for (Entry** q = sortedEnumerables.data(); q != p; ++q) + propertyNames.add(Identifier(q[0]->key)); +} + +void PropertyMap::save(SavedProperties& s) const +{ + unsigned count = 0; + + if (!m_usingTable) { +#if USE_SINGLE_ENTRY + if (m_singleEntryKey && !(m_singleEntryAttributes & (ReadOnly | Function))) + ++count; +#endif + } else { + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; ++i) + if (m_u.table->entries()[i].key && !(m_u.table->entries()[i].attributes & (ReadOnly | Function))) + ++count; + } + + s.properties.clear(); + s.count = count; + + if (count == 0) + return; + + s.properties.set(new SavedProperty[count]); + + SavedProperty* prop = s.properties.get(); + +#if USE_SINGLE_ENTRY + if (!m_usingTable) { + prop->init(m_singleEntryKey, m_u.singleEntryValue, m_singleEntryAttributes); + return; + } +#endif + + // Save in the right order so we don't lose the order. + // Another possibility would be to save the indices. + + // Allocate a buffer to use to sort the keys. + Vector<Entry*, smallMapThreshold> sortedEntries(count); + + // Get pointers to the entries in the buffer. + Entry** p = sortedEntries.data(); + unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; ++i) { + if (m_u.table->entries()[i].key && !(m_u.table->entries()[i].attributes & (ReadOnly | Function))) + *p++ = &m_u.table->entries()[i]; + } + ASSERT(p == sortedEntries.data() + count); + + // Sort the entries by index. + qsort(sortedEntries.data(), p - sortedEntries.data(), sizeof(Entry*), comparePropertyMapEntryIndices); + + // Put the sorted entries into the saved properties list. + for (Entry** q = sortedEntries.data(); q != p; ++q, ++prop) { + Entry* e = *q; + prop->init(e->key, e->value, e->attributes); + } +} + +void PropertyMap::restore(const SavedProperties& p) +{ + for (unsigned i = 0; i != p.count; ++i) + put(Identifier(p.properties[i].name()), p.properties[i].value(), p.properties[i].attributes()); +} + +#if DO_PROPERTYMAP_CONSTENCY_CHECK + +void PropertyMap::checkConsistency() +{ + if (!m_usingTable) + return; + + ASSERT(m_u.table->size >= 16); + ASSERT(m_u.table->sizeMask); + ASSERT(m_u.table->size == m_u.table->sizeMask + 1); + ASSERT(!(m_u.table->size & m_u.table->sizeMask)); + + ASSERT(m_u.table->keyCount <= m_u.table->size / 2); + ASSERT(m_u.table->deletedSentinelCount <= m_u.table->size / 4); + + ASSERT(m_u.table->keyCount + m_u.table->deletedSentinelCount <= m_u.table->size / 2); + + unsigned indexCount = 0; + unsigned deletedIndexCount = 0; + for (unsigned a = 0; a != m_u.table->size; ++a) { + unsigned entryIndex = m_u.table->entryIndicies[a]; + if (entryIndex == emptyEntryIndex) + continue; + if (entryIndex == deletedSentinelIndex) { + ++deletedIndexCount; + continue; + } + ASSERT(entryIndex > deletedSentinelIndex); + ASSERT(entryIndex - 1 <= m_u.table->keyCount + m_u.table->deletedSentinelCount); + ++indexCount; + + for (unsigned b = a + 1; b != m_u.table->size; ++b) + ASSERT(m_u.table->entryIndicies[b] != entryIndex); + } + ASSERT(indexCount == m_u.table->keyCount); + ASSERT(deletedIndexCount == m_u.table->deletedSentinelCount); + + ASSERT(m_u.table->entries()[0].key == 0); + + unsigned nonEmptyEntryCount = 0; + for (unsigned c = 1; c <= m_u.table->keyCount + m_u.table->deletedSentinelCount; ++c) { + UString::Rep* rep = m_u.table->entries()[c].key; + if (!rep) { + ASSERT(m_u.table->entries()[c].value->isUndefined()); + continue; + } + ++nonEmptyEntryCount; + unsigned i = rep->computedHash(); + unsigned k = 0; + unsigned entryIndex; + while (1) { + entryIndex = m_u.table->entryIndicies[i & m_u.table->sizeMask]; + ASSERT(entryIndex != emptyEntryIndex); + if (rep == m_u.table->entries()[entryIndex - 1].key) + break; + if (k == 0) + k = 1 | doubleHash(rep->computedHash()); + i += k; + } + ASSERT(entryIndex == c + 1); + } + + ASSERT(nonEmptyEntryCount == m_u.table->keyCount); +} + +#endif // DO_PROPERTYMAP_CONSTENCY_CHECK + +} // namespace KJS diff --git a/JavaScriptCore/kjs/property_map.h b/JavaScriptCore/kjs/property_map.h new file mode 100644 index 0000000..269e911 --- /dev/null +++ b/JavaScriptCore/kjs/property_map.h @@ -0,0 +1,184 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * 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 KJS_PROPERTY_MAP_H_ +#define KJS_PROPERTY_MAP_H_ + +#include "identifier.h" +#include "protect.h" +#include <wtf/OwnArrayPtr.h> + +namespace KJS { + + class JSObject; + class JSValue; + class PropertyNameArray; + + struct PropertyMapEntry; + struct PropertyMapHashTable; + + class SavedProperty : Noncopyable { + public: + // Since we use this in arrays, we allocate it uninitialized + // and then explicitly initialize. This means we can allocate + // the array without initializing every saved property in the + // array twice. To accomplish this, the class uses data members + // with types that don't have constructors. + SavedProperty(); + void init(UString::Rep* name, JSValue*, unsigned attributes); + ~SavedProperty(); + + UString::Rep* name() const; + JSValue* value() const; + unsigned attributes() const; + + private: + UString::Rep* m_name; + JSValue* m_value; + unsigned m_attributes; + }; + + struct SavedProperties { + SavedProperties(); + ~SavedProperties(); + + unsigned count; + OwnArrayPtr<SavedProperty> properties; + }; + + class PropertyMap : Noncopyable { + public: + PropertyMap(); + ~PropertyMap(); + + void clear(); + + void put(const Identifier&, JSValue*, unsigned attributes, bool checkReadOnly = false); + void remove(const Identifier&); + JSValue* get(const Identifier&) const; + JSValue* get(const Identifier&, unsigned& attributes) const; + JSValue** getLocation(const Identifier& name); + + void mark() const; + void getEnumerablePropertyNames(PropertyNameArray&) const; + + void save(SavedProperties&) const; + void restore(const SavedProperties&); + + bool hasGetterSetterProperties() const { return m_getterSetterFlag; } + void setHasGetterSetterProperties(bool f) { m_getterSetterFlag = f; } + + bool containsGettersOrSetters() const; + + private: + typedef PropertyMapEntry Entry; + typedef PropertyMapHashTable Table; + + static bool keysMatch(const UString::Rep*, const UString::Rep*); + void expand(); + void rehash(); + void rehash(unsigned newTableSize); + void createTable(); + + void insert(const Entry&); + + void checkConsistency(); + + UString::Rep* m_singleEntryKey; + union { + JSValue* singleEntryValue; + Table* table; + } m_u; + + short m_singleEntryAttributes; + bool m_getterSetterFlag : 1; + bool m_usingTable : 1; + }; + + inline PropertyMap::PropertyMap() + : m_singleEntryKey(0) + , m_getterSetterFlag(false) + , m_usingTable(false) + + { + } + + inline SavedProperty::SavedProperty() +#ifndef NDEBUG + : m_name(0) + , m_value(0) + , m_attributes(0) +#endif + { + } + + inline void SavedProperty::init(UString::Rep* name, JSValue* value, unsigned attributes) + { + ASSERT(name); + ASSERT(value); + + ASSERT(!m_name); + ASSERT(!m_value); + ASSERT(!m_attributes); + + m_name = name; + m_value = value; + m_attributes = attributes; + name->ref(); + gcProtect(value); + } + + inline SavedProperty::~SavedProperty() + { + ASSERT(m_name); + ASSERT(m_value); + + m_name->deref(); + gcUnprotect(m_value); + } + + inline UString::Rep* SavedProperty::name() const + { + ASSERT(m_name); + ASSERT(m_value); + + return m_name; + } + + inline JSValue* SavedProperty::value() const + { + ASSERT(m_name); + ASSERT(m_value); + + return m_value; + } + + inline unsigned SavedProperty::attributes() const + { + ASSERT(m_name); + ASSERT(m_value); + + return m_attributes; + } + +} // namespace + +#endif // _KJS_PROPERTY_MAP_H_ diff --git a/JavaScriptCore/kjs/property_slot.cpp b/JavaScriptCore/kjs/property_slot.cpp new file mode 100644 index 0000000..ef2525f --- /dev/null +++ b/JavaScriptCore/kjs/property_slot.cpp @@ -0,0 +1,46 @@ +// -*- c-basic-offset: 4 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2005 Apple Computer, 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 "property_slot.h" +#include "object.h" + +namespace KJS { + +JSValue *PropertySlot::undefinedGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&) +{ + return jsUndefined(); +} + +JSValue* PropertySlot::ungettableGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&) +{ + ASSERT_NOT_REACHED(); + return jsUndefined(); +} + +JSValue *PropertySlot::functionGetter(ExecState* exec, JSObject* originalObject, const Identifier&, const PropertySlot& slot) +{ + return slot.m_data.getterFunc->call(exec, originalObject, exec->emptyList()); +} + +} diff --git a/JavaScriptCore/kjs/property_slot.h b/JavaScriptCore/kjs/property_slot.h new file mode 100644 index 0000000..4ec8b46 --- /dev/null +++ b/JavaScriptCore/kjs/property_slot.h @@ -0,0 +1,142 @@ +// -*- c-basic-offset: 4 -*- +/* + * Copyright (C) 2005, 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 KJS_PROPERTY_SLOT_H +#define KJS_PROPERTY_SLOT_H + +#include "identifier.h" +#include "value.h" +#include <wtf/Assertions.h> + +namespace KJS { + +class ExecState; +class JSObject; + +struct HashEntry; + +#define KJS_VALUE_SLOT_MARKER 0 +#define KJS_NUMERIC_PROPERTY_NAME_SLOT_MARKER reinterpret_cast<GetValueFunc>(1) + +class PropertySlot { +public: + typedef JSValue* (*GetValueFunc)(ExecState*, JSObject* originalObject, const Identifier&, const PropertySlot&); + typedef JSValue* (*GetValueNumericFunc)(ExecState*, JSObject* originalObject, unsigned index, const PropertySlot&); + + JSValue* getValue(ExecState* exec, JSObject* originalObject, const Identifier& propertyName) const + { + if (m_getValue == KJS_VALUE_SLOT_MARKER) + return *m_data.valueSlot; + ASSERT(m_getValue != KJS_NUMERIC_PROPERTY_NAME_SLOT_MARKER); + return m_getValue(exec, originalObject, propertyName, *this); + } + + JSValue* getValue(ExecState* exec, JSObject* originalObject, unsigned propertyName) const + { + if (m_getValue == KJS_VALUE_SLOT_MARKER) + return *m_data.valueSlot; + if (m_getValue == KJS_NUMERIC_PROPERTY_NAME_SLOT_MARKER) + return m_data.numericFunc(exec, originalObject, propertyName, *this); + return m_getValue(exec, originalObject, Identifier::from(propertyName), *this); + } + + void setValueSlot(JSObject* slotBase, JSValue** valueSlot) + { + m_getValue = KJS_VALUE_SLOT_MARKER; + m_slotBase = slotBase; + m_data.valueSlot = valueSlot; + } + + void setStaticEntry(JSObject* slotBase, const HashEntry* staticEntry, GetValueFunc getValue) + { + ASSERT(getValue); + m_getValue = getValue; + m_slotBase = slotBase; + m_data.staticEntry = staticEntry; + } + + void setCustom(JSObject* slotBase, GetValueFunc getValue) + { + ASSERT(getValue); + m_getValue = getValue; + m_slotBase = slotBase; + } + + void setCustomIndex(JSObject* slotBase, unsigned index, GetValueFunc getValue) + { + ASSERT(getValue); + m_getValue = getValue; + m_slotBase = slotBase; + m_data.index = index; + } + + void setCustomNumeric(JSObject* slotBase, GetValueNumericFunc getValue) + { + ASSERT(getValue); + m_slotBase = slotBase; + m_getValue = KJS_NUMERIC_PROPERTY_NAME_SLOT_MARKER; + m_data.numericFunc = getValue; + } + + void setGetterSlot(JSObject* slotBase, JSObject* getterFunc) + { + m_getValue = functionGetter; + m_slotBase = slotBase; + m_data.getterFunc = getterFunc; + } + + void setUndefined(JSObject *slotBase) + { + m_slotBase = slotBase; + m_getValue = undefinedGetter; + } + + void setUngettable(JSObject* slotBase) // Used to signal that you have a property, but trying to get it at this time is an error. + { + m_slotBase = slotBase; + m_getValue = ungettableGetter; + } + + JSObject* slotBase() const { return m_slotBase; } + + const HashEntry* staticEntry() const { return m_data.staticEntry; } + unsigned index() const { return m_data.index; } + +private: + static JSValue* undefinedGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + static JSValue* ungettableGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + static JSValue* functionGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&); + + GetValueFunc m_getValue; + + JSObject* m_slotBase; + union { + JSObject* getterFunc; + JSValue** valueSlot; + const HashEntry* staticEntry; + unsigned index; + GetValueNumericFunc numericFunc; + } m_data; +}; + +} + +#endif // KJS_PROPERTY_SLOT_H diff --git a/JavaScriptCore/kjs/protect.h b/JavaScriptCore/kjs/protect.h new file mode 100644 index 0000000..912e45c --- /dev/null +++ b/JavaScriptCore/kjs/protect.h @@ -0,0 +1,143 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2004 Apple Computer, 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. + * + */ + + +#ifndef _KJS_PROTECT_H_ +#define _KJS_PROTECT_H_ + +#include "value.h" +#include "collector.h" +#include "JSLock.h" + +namespace KJS { + + inline void gcProtect(JSValue *val) + { + Collector::protect(val); + } + + inline void gcUnprotect(JSValue *val) + { + Collector::unprotect(val); + } + + inline void gcProtectNullTolerant(JSValue *val) + { + if (val) + gcProtect(val); + } + + inline void gcUnprotectNullTolerant(JSValue *val) + { + if (val) + gcUnprotect(val); + } + + // FIXME: Share more code with RefPtr template? The only differences are the ref/deref operation + // and the implicit conversion to raw pointer + template <class T> class ProtectedPtr { + public: + ProtectedPtr() : m_ptr(NULL) { } + ProtectedPtr(T *ptr); + ProtectedPtr(const ProtectedPtr &); + ~ProtectedPtr(); + + template <class U> ProtectedPtr(const ProtectedPtr<U> &); + + T *get() const { return m_ptr; } + operator T *() const { return m_ptr; } + T *operator->() const { return m_ptr; } + + bool operator!() const { return m_ptr == NULL; } + + ProtectedPtr &operator=(const ProtectedPtr &); + ProtectedPtr &operator=(T *); + + private: + T *m_ptr; + }; + + template <class T> ProtectedPtr<T>::ProtectedPtr(T *ptr) + : m_ptr(ptr) + { + if (ptr) { + JSLock lock; + gcProtect(ptr); + } + } + + template <class T> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr &o) + : m_ptr(o.get()) + { + if (T *ptr = m_ptr) { + JSLock lock; + gcProtect(ptr); + } + } + + template <class T> ProtectedPtr<T>::~ProtectedPtr() + { + if (T *ptr = m_ptr) { + JSLock lock; + gcUnprotect(ptr); + } + } + + template <class T> template <class U> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr<U> &o) + : m_ptr(o.get()) + { + if (T *ptr = m_ptr) { + JSLock lock; + gcProtect(ptr); + } + } + + template <class T> ProtectedPtr<T> &ProtectedPtr<T>::operator=(const ProtectedPtr<T> &o) + { + JSLock lock; + T *optr = o.m_ptr; + gcProtectNullTolerant(optr); + gcUnprotectNullTolerant(m_ptr); + m_ptr = optr; + return *this; + } + + template <class T> inline ProtectedPtr<T> &ProtectedPtr<T>::operator=(T *optr) + { + JSLock lock; + gcProtectNullTolerant(optr); + gcUnprotectNullTolerant(m_ptr); + m_ptr = optr; + return *this; + } + + template <class T> inline bool operator==(const ProtectedPtr<T> &a, const ProtectedPtr<T> &b) { return a.get() == b.get(); } + template <class T> inline bool operator==(const ProtectedPtr<T> &a, const T *b) { return a.get() == b; } + template <class T> inline bool operator==(const T *a, const ProtectedPtr<T> &b) { return a == b.get(); } + + template <class T> inline bool operator!=(const ProtectedPtr<T> &a, const ProtectedPtr<T> &b) { return a.get() != b.get(); } + template <class T> inline bool operator!=(const ProtectedPtr<T> &a, const T *b) { return a.get() != b; } + template <class T> inline bool operator!=(const T *a, const ProtectedPtr<T> &b) { return a != b.get(); } + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/regexp.cpp b/JavaScriptCore/kjs/regexp.cpp new file mode 100644 index 0000000..4fbc645 --- /dev/null +++ b/JavaScriptCore/kjs/regexp.cpp @@ -0,0 +1,131 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2001, 2004 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 "regexp.h" + +#include "lexer.h" +#include <pcre/pcre.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> + +namespace KJS { + +inline RegExp::RegExp(const UString& pattern) + : m_pattern(pattern) + , m_flagBits(0) + , m_constructionError(0) + , m_numSubpatterns(0) +{ + m_regExp = jsRegExpCompile(reinterpret_cast<const ::UChar*>(pattern.data()), pattern.size(), + JSRegExpDoNotIgnoreCase, JSRegExpSingleLine, &m_numSubpatterns, &m_constructionError); +} + +PassRefPtr<RegExp> RegExp::create(const UString& pattern) +{ + return adoptRef(new RegExp(pattern)); +} + +inline RegExp::RegExp(const UString& pattern, const UString& flags) + : m_pattern(pattern) + , m_flags(flags) + , m_flagBits(0) + , m_constructionError(0) + , m_numSubpatterns(0) +{ + // NOTE: The global flag is handled on a case-by-case basis by functions like + // String::match and RegExpImp::match. + if (flags.find('g') != -1) + m_flagBits |= Global; + + // FIXME: Eliminate duplication by adding a way ask a JSRegExp what its flags are? + JSRegExpIgnoreCaseOption ignoreCaseOption = JSRegExpDoNotIgnoreCase; + if (flags.find('i') != -1) { + m_flagBits |= IgnoreCase; + ignoreCaseOption = JSRegExpIgnoreCase; + } + + JSRegExpMultilineOption multilineOption = JSRegExpSingleLine; + if (flags.find('m') != -1) { + m_flagBits |= Multiline; + multilineOption = JSRegExpMultiline; + } + + m_regExp = jsRegExpCompile(reinterpret_cast<const ::UChar*>(pattern.data()), pattern.size(), + ignoreCaseOption, multilineOption, &m_numSubpatterns, &m_constructionError); +} + +PassRefPtr<RegExp> RegExp::create(const UString& pattern, const UString& flags) +{ + return adoptRef(new RegExp(pattern, flags)); +} + +RegExp::~RegExp() +{ + jsRegExpFree(m_regExp); +} + +int RegExp::match(const UString& s, int i, OwnArrayPtr<int>* ovector) +{ + if (i < 0) + i = 0; + if (ovector) + ovector->clear(); + + if (i > s.size() || s.isNull()) + return -1; + + if (!m_regExp) + return -1; + + // Set up the offset vector for the result. + // First 2/3 used for result, the last third used by PCRE. + int* offsetVector; + int offsetVectorSize; + int fixedSizeOffsetVector[3]; + if (!ovector) { + offsetVectorSize = 3; + offsetVector = fixedSizeOffsetVector; + } else { + offsetVectorSize = (m_numSubpatterns + 1) * 3; + offsetVector = new int [offsetVectorSize]; + ovector->set(offsetVector); + } + + int numMatches = jsRegExpExecute(m_regExp, reinterpret_cast<const ::UChar*>(s.data()), s.size(), i, offsetVector, offsetVectorSize); + + if (numMatches < 0) { +#ifndef NDEBUG + if (numMatches != JSRegExpErrorNoMatch) + fprintf(stderr, "jsRegExpExecute failed with result %d\n", numMatches); +#endif + if (ovector) + ovector->clear(); + return -1; + } + + return offsetVector[0]; +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/regexp.h b/JavaScriptCore/kjs/regexp.h new file mode 100644 index 0000000..1930ac9 --- /dev/null +++ b/JavaScriptCore/kjs/regexp.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef KJS_REGEXP_H +#define KJS_REGEXP_H + +#include "ustring.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +struct JSRegExp; + +namespace KJS { + + class RegExp : public RefCounted<RegExp> { + public: + static PassRefPtr<RegExp> create(const UString& pattern); + static PassRefPtr<RegExp> create(const UString& pattern, const UString& flags); + ~RegExp(); + + bool global() const { return m_flagBits & Global; } + bool ignoreCase() const { return m_flagBits & IgnoreCase; } + bool multiline() const { return m_flagBits & Multiline; } + + const UString& pattern() const { return m_pattern; } + const UString& flags() const { return m_flags; } + + bool isValid() const { return !m_constructionError; } + const char* errorMessage() const { return m_constructionError; } + + int match(const UString&, int offset, OwnArrayPtr<int>* ovector = 0); + unsigned numSubpatterns() const { return m_numSubpatterns; } + + private: + RegExp(const UString& pattern); + RegExp(const UString& pattern, const UString& flags); + + void compile(); + + enum FlagBits { Global = 1, IgnoreCase = 2, Multiline = 4 }; + + UString m_pattern; // FIXME: Just decompile m_regExp instead of storing this. + UString m_flags; // FIXME: Just decompile m_regExp instead of storing this. + int m_flagBits; + JSRegExp* m_regExp; + const char* m_constructionError; + unsigned m_numSubpatterns; + }; + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/regexp_object.cpp b/JavaScriptCore/kjs/regexp_object.cpp new file mode 100644 index 0000000..871d764 --- /dev/null +++ b/JavaScriptCore/kjs/regexp_object.cpp @@ -0,0 +1,475 @@ +/* + * 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 "regexp_object.h" +#include "regexp_object.lut.h" + +#include "array_instance.h" +#include "array_object.h" +#include "error_object.h" +#include "internal.h" +#include "object.h" +#include "operations.h" +#include "regexp.h" +#include "types.h" +#include "value.h" +#include "UnusedParam.h" + +#include <stdio.h> + +namespace KJS { + +// ------------------------------ RegExpPrototype --------------------------- + +static JSValue* regExpProtoFuncTest(ExecState*, JSObject*, const List&); +static JSValue* regExpProtoFuncExec(ExecState*, JSObject*, const List&); +static JSValue* regExpProtoFuncCompile(ExecState*, JSObject*, const List&); +static JSValue* regExpProtoFuncToString(ExecState*, JSObject*, const List&); + +// ECMA 15.10.5 + +const ClassInfo RegExpPrototype::info = { "RegExpPrototype", 0, 0 }; + +RegExpPrototype::RegExpPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype) + : JSObject(objectPrototype) +{ + static const Identifier* compilePropertyName = new Identifier("compile"); + static const Identifier* execPropertyName = new Identifier("exec"); + static const Identifier* testPropertyName = new Identifier("test"); + + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, *compilePropertyName, regExpProtoFuncCompile), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, *execPropertyName, regExpProtoFuncExec), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, *testPropertyName, regExpProtoFuncTest), DontEnum); + putDirectFunction(new PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toString, regExpProtoFuncToString), DontEnum); +} + +// ------------------------------ Functions --------------------------- + +JSValue* regExpProtoFuncTest(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&RegExpImp::info)) + return throwError(exec, TypeError); + + return static_cast<RegExpImp*>(thisObj)->test(exec, args); +} + +JSValue* regExpProtoFuncExec(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&RegExpImp::info)) + return throwError(exec, TypeError); + + return static_cast<RegExpImp*>(thisObj)->exec(exec, args); +} + +JSValue* regExpProtoFuncCompile(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (!thisObj->inherits(&RegExpImp::info)) + return throwError(exec, TypeError); + + RefPtr<RegExp> regExp; + JSValue* arg0 = args[0]; + JSValue* arg1 = args[1]; + + if (arg0->isObject(&RegExpImp::info)) { + if (!arg1->isUndefined()) + return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); + regExp = static_cast<RegExpImp*>(arg0)->regExp(); + } else { + UString pattern = args.isEmpty() ? UString("") : arg0->toString(exec); + UString flags = arg1->isUndefined() ? UString("") : arg1->toString(exec); + regExp = RegExp::create(pattern, flags); + } + + if (!regExp->isValid()) + return throwError(exec, SyntaxError, UString("Invalid regular expression: ").append(regExp->errorMessage())); + + static_cast<RegExpImp*>(thisObj)->setRegExp(regExp.release()); + static_cast<RegExpImp*>(thisObj)->setLastIndex(0); + return jsUndefined(); +} + +JSValue* regExpProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&RegExpImp::info)) { + if (thisObj->inherits(&RegExpPrototype::info)) + return jsString("//"); + return throwError(exec, TypeError); + } + + UString result = "/" + thisObj->get(exec, exec->propertyNames().source)->toString(exec) + "/"; + if (thisObj->get(exec, exec->propertyNames().global)->toBoolean(exec)) + result += "g"; + if (thisObj->get(exec, exec->propertyNames().ignoreCase)->toBoolean(exec)) + result += "i"; + if (thisObj->get(exec, exec->propertyNames().multiline)->toBoolean(exec)) + result += "m"; + return jsString(result); +} + +// ------------------------------ RegExpImp ------------------------------------ + +const ClassInfo RegExpImp::info = { "RegExp", 0, &RegExpImpTable }; + +/* Source for regexp_object.lut.h +@begin RegExpImpTable 5 + global RegExpImp::Global DontDelete|ReadOnly|DontEnum + ignoreCase RegExpImp::IgnoreCase DontDelete|ReadOnly|DontEnum + multiline RegExpImp::Multiline DontDelete|ReadOnly|DontEnum + source RegExpImp::Source DontDelete|ReadOnly|DontEnum + lastIndex RegExpImp::LastIndex DontDelete|DontEnum +@end +*/ + +RegExpImp::RegExpImp(RegExpPrototype* regexpProto, PassRefPtr<RegExp> regExp) + : JSObject(regexpProto) + , m_regExp(regExp) + , m_lastIndex(0) +{ +} + +RegExpImp::~RegExpImp() +{ +} + +bool RegExpImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<RegExpImp, JSObject>(exec, &RegExpImpTable, this, propertyName, slot); +} + +JSValue* RegExpImp::getValueProperty(ExecState*, int token) const +{ + switch (token) { + case Global: + return jsBoolean(m_regExp->global()); + case IgnoreCase: + return jsBoolean(m_regExp->ignoreCase()); + case Multiline: + return jsBoolean(m_regExp->multiline()); + case Source: + return jsString(m_regExp->pattern()); + case LastIndex: + return jsNumber(m_lastIndex); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void RegExpImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + lookupPut<RegExpImp, JSObject>(exec, propertyName, value, &RegExpImpTable, this); +} + +void RegExpImp::putValueProperty(ExecState* exec, int token, JSValue* value) +{ + UNUSED_PARAM(token); + ASSERT(token == LastIndex); + m_lastIndex = value->toInteger(exec); +} + +bool RegExpImp::match(ExecState* exec, const List& args) +{ + RegExpObjectImp* regExpObj = exec->lexicalGlobalObject()->regExpConstructor(); + + UString input; + if (!args.isEmpty()) + input = args[0]->toString(exec); + else { + input = regExpObj->input(); + if (input.isNull()) { + throwError(exec, GeneralError, "No input."); + return false; + } + } + + bool global = get(exec, exec->propertyNames().global)->toBoolean(exec); + int lastIndex = 0; + if (global) { + if (m_lastIndex < 0 || m_lastIndex > input.size()) { + m_lastIndex = 0; + return false; + } + lastIndex = static_cast<int>(m_lastIndex); + } + + int foundIndex; + int foundLength; + regExpObj->performMatch(m_regExp.get(), input, lastIndex, foundIndex, foundLength); + + if (global) { + lastIndex = foundIndex < 0 ? 0 : foundIndex + foundLength; + m_lastIndex = lastIndex; + } + + return foundIndex >= 0; +} + +JSValue* RegExpImp::test(ExecState* exec, const List& args) +{ + return jsBoolean(match(exec, args)); +} + +JSValue* RegExpImp::exec(ExecState* exec, const List& args) +{ + return match(exec, args) + ? exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec) + : jsNull(); +} + +bool RegExpImp::implementsCall() const +{ + return true; +} + +JSValue* RegExpImp::callAsFunction(ExecState* exec, JSObject*, const List& args) +{ + return RegExpImp::exec(exec, args); +} + +// ------------------------------ RegExpObjectImp ------------------------------ + +const ClassInfo RegExpObjectImp::info = { "Function", &InternalFunctionImp::info, &RegExpObjectImpTable }; + +/* Source for regexp_object.lut.h +@begin RegExpObjectImpTable 21 + input RegExpObjectImp::Input None + $_ RegExpObjectImp::Input DontEnum + multiline RegExpObjectImp::Multiline None + $* RegExpObjectImp::Multiline DontEnum + lastMatch RegExpObjectImp::LastMatch DontDelete|ReadOnly + $& RegExpObjectImp::LastMatch DontDelete|ReadOnly|DontEnum + lastParen RegExpObjectImp::LastParen DontDelete|ReadOnly + $+ RegExpObjectImp::LastParen DontDelete|ReadOnly|DontEnum + leftContext RegExpObjectImp::LeftContext DontDelete|ReadOnly + $` RegExpObjectImp::LeftContext DontDelete|ReadOnly|DontEnum + rightContext RegExpObjectImp::RightContext DontDelete|ReadOnly + $' RegExpObjectImp::RightContext DontDelete|ReadOnly|DontEnum + $1 RegExpObjectImp::Dollar1 DontDelete|ReadOnly + $2 RegExpObjectImp::Dollar2 DontDelete|ReadOnly + $3 RegExpObjectImp::Dollar3 DontDelete|ReadOnly + $4 RegExpObjectImp::Dollar4 DontDelete|ReadOnly + $5 RegExpObjectImp::Dollar5 DontDelete|ReadOnly + $6 RegExpObjectImp::Dollar6 DontDelete|ReadOnly + $7 RegExpObjectImp::Dollar7 DontDelete|ReadOnly + $8 RegExpObjectImp::Dollar8 DontDelete|ReadOnly + $9 RegExpObjectImp::Dollar9 DontDelete|ReadOnly +@end +*/ + +struct RegExpObjectImpPrivate { + // Global search cache / settings + RegExpObjectImpPrivate() : lastNumSubPatterns(0), multiline(false) { } + UString lastInput; + OwnArrayPtr<int> lastOvector; + unsigned lastNumSubPatterns : 31; + bool multiline : 1; +}; + +RegExpObjectImp::RegExpObjectImp(ExecState* exec, FunctionPrototype* funcProto, RegExpPrototype* regProto) + : InternalFunctionImp(funcProto, "RegExp") + , d(new RegExpObjectImpPrivate) +{ + // ECMA 15.10.5.1 RegExp.prototype + putDirect(exec->propertyNames().prototype, regProto, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(2), ReadOnly | DontDelete | DontEnum); +} + +/* + 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. +*/ +void RegExpObjectImp::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) +{ + OwnArrayPtr<int> tmpOvector; + position = r->match(s, startOffset, &tmpOvector); + + if (ovector) + *ovector = tmpOvector.get(); + + if (position != -1) { + ASSERT(tmpOvector); + + length = tmpOvector[1] - tmpOvector[0]; + + d->lastInput = s; + d->lastOvector.set(tmpOvector.release()); + d->lastNumSubPatterns = r->numSubpatterns(); + } +} + +JSObject* RegExpObjectImp::arrayOfMatches(ExecState* exec) const +{ + unsigned lastNumSubpatterns = d->lastNumSubPatterns; + ArrayInstance* arr = new ArrayInstance(exec->lexicalGlobalObject()->arrayPrototype(), lastNumSubpatterns + 1); + for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { + int start = d->lastOvector[2 * i]; + if (start >= 0) + arr->put(exec, i, jsString(d->lastInput.substr(start, d->lastOvector[2 * i + 1] - start))); + } + arr->put(exec, exec->propertyNames().index, jsNumber(d->lastOvector[0])); + arr->put(exec, exec->propertyNames().input, jsString(d->lastInput)); + return arr; +} + +JSValue* RegExpObjectImp::getBackref(unsigned i) const +{ + if (d->lastOvector && i <= d->lastNumSubPatterns) + return jsString(d->lastInput.substr(d->lastOvector[2 * i], d->lastOvector[2 * i + 1] - d->lastOvector[2 * i])); + return jsString(""); +} + +JSValue* RegExpObjectImp::getLastParen() const +{ + unsigned i = d->lastNumSubPatterns; + if (i > 0) { + ASSERT(d->lastOvector); + return jsString(d->lastInput.substr(d->lastOvector[2 * i], d->lastOvector[2 * i + 1] - d->lastOvector[2 * i])); + } + return jsString(""); +} + +JSValue *RegExpObjectImp::getLeftContext() const +{ + if (d->lastOvector) + return jsString(d->lastInput.substr(0, d->lastOvector[0])); + return jsString(""); +} + +JSValue *RegExpObjectImp::getRightContext() const +{ + if (d->lastOvector) { + UString s = d->lastInput; + return jsString(s.substr(d->lastOvector[1], s.size() - d->lastOvector[1])); + } + return jsString(""); +} + +bool RegExpObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<RegExpObjectImp, InternalFunctionImp>(exec, &RegExpObjectImpTable, this, propertyName, slot); +} + +JSValue *RegExpObjectImp::getValueProperty(ExecState*, int token) const +{ + switch (token) { + case Dollar1: + return getBackref(1); + case Dollar2: + return getBackref(2); + case Dollar3: + return getBackref(3); + case Dollar4: + return getBackref(4); + case Dollar5: + return getBackref(5); + case Dollar6: + return getBackref(6); + case Dollar7: + return getBackref(7); + case Dollar8: + return getBackref(8); + case Dollar9: + return getBackref(9); + case Input: + return jsString(d->lastInput); + case Multiline: + return jsBoolean(d->multiline); + case LastMatch: + return getBackref(0); + case LastParen: + return getLastParen(); + case LeftContext: + return getLeftContext(); + case RightContext: + return getRightContext(); + default: + ASSERT(0); + } + + return jsString(""); +} + +void RegExpObjectImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value) +{ + lookupPut<RegExpObjectImp, InternalFunctionImp>(exec, propertyName, value, &RegExpObjectImpTable, this); +} + +void RegExpObjectImp::putValueProperty(ExecState *exec, int token, JSValue *value) +{ + switch (token) { + case Input: + d->lastInput = value->toString(exec); + break; + case Multiline: + d->multiline = value->toBoolean(exec); + break; + default: + ASSERT(0); + } +} + +bool RegExpObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.10.4 +JSObject *RegExpObjectImp::construct(ExecState *exec, const List &args) +{ + JSValue* arg0 = args[0]; + JSValue* arg1 = args[1]; + + if (arg0->isObject(&RegExpImp::info)) { + if (!arg1->isUndefined()) + return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); + return static_cast<JSObject*>(arg0); + } + + UString pattern = arg0->isUndefined() ? UString("") : arg0->toString(exec); + UString flags = arg1->isUndefined() ? UString("") : arg1->toString(exec); + + return createRegExpImp(exec, RegExp::create(pattern, flags)); +} + +JSObject* RegExpObjectImp::createRegExpImp(ExecState* exec, PassRefPtr<RegExp> regExp) +{ + return regExp->isValid() + ? new RegExpImp(static_cast<RegExpPrototype*>(exec->lexicalGlobalObject()->regExpPrototype()), regExp) + : throwError(exec, SyntaxError, UString("Invalid regular expression: ").append(regExp->errorMessage())); +} + +// ECMA 15.10.3 +JSValue *RegExpObjectImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args) +{ + return construct(exec, args); +} + +const UString& RegExpObjectImp::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->lastInput; +} + +} diff --git a/JavaScriptCore/kjs/regexp_object.h b/JavaScriptCore/kjs/regexp_object.h new file mode 100644 index 0000000..c69e8f7 --- /dev/null +++ b/JavaScriptCore/kjs/regexp_object.h @@ -0,0 +1,105 @@ +/* + * 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 REGEXP_OBJECT_H_ +#define REGEXP_OBJECT_H_ + +#include "function_object.h" +#include "regexp.h" + +namespace KJS { + + struct RegExpObjectImpPrivate; + + class RegExpPrototype : public JSObject { + public: + RegExpPrototype(ExecState*, ObjectPrototype*, FunctionPrototype*); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + class RegExpImp : public JSObject { + public: + enum { Global, IgnoreCase, Multiline, Source, LastIndex }; + + RegExpImp(RegExpPrototype*, PassRefPtr<RegExp>); + virtual ~RegExpImp(); + + void setRegExp(PassRefPtr<RegExp> r) { m_regExp = r; } + RegExp* regExp() const { return m_regExp.get(); } + + JSValue* test(ExecState*, const List& args); + JSValue* exec(ExecState*, const List& args); + + virtual bool implementsCall() const; + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + JSValue* getValueProperty(ExecState*, int token) const; + void put(ExecState*, const Identifier&, JSValue*); + void putValueProperty(ExecState*, int token, JSValue*); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + + void setLastIndex(double lastIndex) { m_lastIndex = lastIndex; } + + private: + bool match(ExecState*, const List& args); + + RefPtr<RegExp> m_regExp; + double m_lastIndex; + }; + + class RegExpObjectImp : public InternalFunctionImp { + public: + enum { Dollar1, Dollar2, Dollar3, Dollar4, Dollar5, Dollar6, Dollar7, Dollar8, Dollar9, + Input, Multiline, LastMatch, LastParen, LeftContext, RightContext }; + + RegExpObjectImp(ExecState*, FunctionPrototype*, RegExpPrototype*); + + virtual bool implementsConstruct() const; + virtual JSObject* construct(ExecState*, const List&); + JSObject* createRegExpImp(ExecState*, PassRefPtr<RegExp>); + virtual JSValue* callAsFunction(ExecState*, JSObject*, const List&); + virtual void put(ExecState*, const Identifier&, JSValue*); + void putValueProperty(ExecState*, int token, JSValue*); + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + JSValue* getValueProperty(ExecState*, int token) const; + virtual const ClassInfo* classInfo() const { return &info; } + + void performMatch(RegExp*, const UString&, int startOffset, int& position, int& length, int** ovector = 0); + JSObject* arrayOfMatches(ExecState*) const; + const UString& input() const; + + private: + JSValue* getBackref(unsigned) const; + JSValue* getLastParen() const; + JSValue* getLeftContext() const; + JSValue* getRightContext() const; + + OwnPtr<RegExpObjectImpPrivate> d; + + static const ClassInfo info; + }; + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/scope_chain.cpp b/JavaScriptCore/kjs/scope_chain.cpp new file mode 100644 index 0000000..aba066a --- /dev/null +++ b/JavaScriptCore/kjs/scope_chain.cpp @@ -0,0 +1,63 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2003, 2006 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 "scope_chain.h" +#include "PropertyNameArray.h" +#include <stdio.h> +#include "object.h" + +namespace KJS { + +void ScopeChain::push(const ScopeChain &c) +{ + ScopeChainNode **tail = &_node; + for (ScopeChainNode *n = c._node; n; n = n->next) { + ScopeChainNode *newNode = new ScopeChainNode(*tail, n->object); + *tail = newNode; + tail = &newNode->next; + } +} + +#ifndef NDEBUG + +void ScopeChain::print() +{ + ScopeChainIterator scopeEnd = end(); + for (ScopeChainIterator scopeIter = begin(); scopeIter != scopeEnd; ++scopeIter) { + JSObject* o = *scopeIter; + PropertyNameArray propertyNames; + // FIXME: should pass ExecState here! + o->getPropertyNames(0, propertyNames); + PropertyNameArray::const_iterator propEnd = propertyNames.end(); + + fprintf(stderr, "----- [scope %p] -----\n", o); + for (PropertyNameArray::const_iterator propIter = propertyNames.begin(); propIter != propEnd; propIter++) { + Identifier name = *propIter; + fprintf(stderr, "%s, ", name.ascii()); + } + fprintf(stderr, "\n"); + } +} + +#endif + +} // namespace KJS diff --git a/JavaScriptCore/kjs/scope_chain.h b/JavaScriptCore/kjs/scope_chain.h new file mode 100644 index 0000000..7441cb8 --- /dev/null +++ b/JavaScriptCore/kjs/scope_chain.h @@ -0,0 +1,156 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Apple Computer, 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. + * + */ + +#ifndef KJS_SCOPE_CHAIN_H +#define KJS_SCOPE_CHAIN_H + +#include <wtf/Assertions.h> + +namespace KJS { + + class JSObject; + class ExecState; + + class ScopeChainNode { + public: + ScopeChainNode(ScopeChainNode *n, JSObject *o) + : next(n), object(o), refCount(1) { } + + ScopeChainNode *next; + JSObject *object; + int refCount; + }; + + class ScopeChainIterator { + public: + ScopeChainIterator(ScopeChainNode *node) : m_node(node) {} + + JSObject * const & operator*() const { return m_node->object; } + JSObject * const * operator->() const { return &(operator*()); } + + ScopeChainIterator& operator++() { m_node = m_node->next; return *this; } + + // postfix ++ intentionally omitted + + bool operator==(const ScopeChainIterator& other) const { return m_node == other.m_node; } + bool operator!=(const ScopeChainIterator& other) const { return m_node != other.m_node; } + + private: + ScopeChainNode *m_node; + }; + + class ScopeChain { + public: + typedef ScopeChainIterator const_iterator; + typedef JSObject* ValueType; + + ScopeChain() : _node(0) { } + ~ScopeChain() { deref(); } + + ScopeChain(const ScopeChain &c) : _node(c._node) + { if (_node) ++_node->refCount; } + ScopeChain &operator=(const ScopeChain &); + + bool isEmpty() const { return !_node; } + JSObject *top() const { return _node->object; } + + JSObject *bottom() const; + + ScopeChainIterator begin() const { return ScopeChainIterator(_node); } + ScopeChainIterator end() const { return ScopeChainIterator(0); } + + void clear() { deref(); _node = 0; } + void push(JSObject *); + void push(const ScopeChain &); + void replaceTop(JSObject*); + void pop(); + + void mark(); + +#ifndef NDEBUG + void print(); +#endif + + private: + ScopeChainNode *_node; + + void deref() { if (_node && --_node->refCount == 0) release(); } + void ref() const; + + void release(); + }; + +inline void ScopeChain::ref() const +{ + for (ScopeChainNode *n = _node; n; n = n->next) { + if (n->refCount++ != 0) + break; + } +} + +inline ScopeChain &ScopeChain::operator=(const ScopeChain &c) +{ + c.ref(); + deref(); + _node = c._node; + return *this; +} + +inline JSObject *ScopeChain::bottom() const +{ + ScopeChainNode *last = 0; + for (ScopeChainNode *n = _node; n; n = n->next) + last = n; + if (!last) + return 0; + return last->object; +} + +inline void ScopeChain::push(JSObject *o) +{ + ASSERT(o); + _node = new ScopeChainNode(_node, o); +} + +inline void ScopeChain::replaceTop(JSObject* o) +{ + ASSERT(o); + _node->object = o; +} + +inline void ScopeChain::pop() +{ + ScopeChainNode *oldNode = _node; + ASSERT(oldNode); + ScopeChainNode *newNode = oldNode->next; + _node = newNode; + + if (--oldNode->refCount != 0) { + if (newNode) + ++newNode->refCount; + } else { + delete oldNode; + } +} + +} // namespace KJS + +#endif // KJS_SCOPE_CHAIN_H diff --git a/JavaScriptCore/kjs/scope_chain_mark.h b/JavaScriptCore/kjs/scope_chain_mark.h new file mode 100644 index 0000000..bc4d5f8 --- /dev/null +++ b/JavaScriptCore/kjs/scope_chain_mark.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef scope_chain_mark_h +#define scope_chain_mark_h + +#include "Activation.h" +#include "scope_chain.h" + +namespace KJS { + + inline void ScopeChain::mark() + { + for (ScopeChainNode* n = _node; n; n = n->next) { + JSObject* o = n->object; + + // An ActivationImp that is on the activation stack can't have the + // JSObject::marked() method called on it, because it doesn't have an + // entry in a GC mark bitmap, so we check here whether it is on the + // stack and directly call the portion of the marking code that is + // still relevant. + + if (o->isActivationObject() && static_cast<ActivationImp*>(o)->isOnStack()) + static_cast<ActivationImp*>(o)->markChildren(); + else if (!o->marked()) + o->mark(); + } + } + +} // namespace KJS + +#endif diff --git a/JavaScriptCore/kjs/string_object.cpp b/JavaScriptCore/kjs/string_object.cpp new file mode 100644 index 0000000..04d8ebb --- /dev/null +++ b/JavaScriptCore/kjs/string_object.cpp @@ -0,0 +1,1051 @@ +// -*- c-basic-offset: 2 -*- +/* + * 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 "string_object.h" +#include "string_object.lut.h" + +#include "JSWrapperObject.h" +#include "PropertyNameArray.h" +#include "array_object.h" +#include "error_object.h" +#include "operations.h" +#include "regexp_object.h" +#include <wtf/MathExtras.h> +#include <wtf/unicode/Unicode.h> + +#if PLATFORM(CF) +#include <CoreFoundation/CoreFoundation.h> +#elif PLATFORM(WIN_OS) +#include <windows.h> +#endif + +using namespace WTF; + +namespace KJS { + +// ------------------------------ StringInstance ---------------------------- + +const ClassInfo StringInstance::info = { "String", 0, 0 }; + +StringInstance::StringInstance(JSObject *proto) + : JSWrapperObject(proto) +{ + setInternalValue(jsString("")); +} + +StringInstance::StringInstance(JSObject *proto, StringImp* string) + : JSWrapperObject(proto) +{ + setInternalValue(string); +} + +StringInstance::StringInstance(JSObject *proto, const UString &string) + : JSWrapperObject(proto) +{ + setInternalValue(jsString(string)); +} + +JSValue *StringInstance::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot &slot) +{ + return jsNumber(static_cast<StringInstance*>(slot.slotBase())->internalValue()->value().size()); +} + +JSValue* StringInstance::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot) +{ + return jsString(static_cast<StringInstance*>(slot.slotBase())->internalValue()->value().substr(slot.index(), 1)); +} + +static JSValue* stringInstanceNumericPropertyGetter(ExecState*, JSObject*, unsigned index, const PropertySlot& slot) +{ + return jsString(static_cast<StringInstance*>(slot.slotBase())->internalValue()->value().substr(index, 1)); +} + +bool StringInstance::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setCustom(this, lengthGetter); + return true; + } + + bool isStrictUInt32; + unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); + unsigned length = internalValue()->value().size(); + if (isStrictUInt32 && i < length) { + slot.setCustomIndex(this, i, indexGetter); + return true; + } + + return JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool StringInstance::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + unsigned length = internalValue()->value().size(); + if (propertyName < length) { + slot.setCustomNumeric(this, stringInstanceNumericPropertyGetter); + return true; + } + + return JSObject::getOwnPropertySlot(exec, Identifier::from(propertyName), slot); +} + +void StringInstance::put(ExecState* exec, const Identifier& propertyName, JSValue* value) +{ + if (propertyName == exec->propertyNames().length) + return; + JSObject::put(exec, propertyName, value); +} + +bool StringInstance::deleteProperty(ExecState *exec, const Identifier &propertyName) +{ + if (propertyName == exec->propertyNames().length) + return false; + return JSObject::deleteProperty(exec, propertyName); +} + +void StringInstance::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +{ + int size = internalValue()->getString().size(); + for (int i = 0; i < size; i++) + propertyNames.add(Identifier(UString::from(i))); + return JSObject::getPropertyNames(exec, propertyNames); +} + +// ------------------------------ StringPrototype --------------------------- +const ClassInfo StringPrototype::info = { "String", &StringInstance::info, &stringTable }; +/* Source for string_object.lut.h +@begin stringTable 26 + toString &stringProtoFuncToString DontEnum|Function 0 + valueOf &stringProtoFuncValueOf 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 + toLocaleLowerCase &stringProtoFuncToLocaleLowerCase DontEnum|Function 0 + toLocaleUpperCase &stringProtoFuncToLocaleUpperCase DontEnum|Function 0 + localeCompare &stringProtoFuncLocaleCompare DontEnum|Function 1 + + 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 +@end +*/ +// ECMA 15.5.4 +StringPrototype::StringPrototype(ExecState* exec, ObjectPrototype* objProto) + : StringInstance(objProto) +{ + // The constructor will be added later, after StringObjectImp has been built + putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +bool StringPrototype::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<StringInstance>(exec, &stringTable, this, propertyName, slot); +} + +// ------------------------------ Functions -------------------------- + +static inline void expandSourceRanges(UString::Range * & array, int& count, int& capacity) +{ + int newCapacity; + if (capacity == 0) { + newCapacity = 16; + } else { + newCapacity = capacity * 2; + } + + UString::Range *newArray = new UString::Range[newCapacity]; + for (int i = 0; i < count; i++) { + newArray[i] = array[i]; + } + + delete [] array; + + capacity = newCapacity; + array = newArray; +} + +static void pushSourceRange(UString::Range * & array, int& count, int& capacity, UString::Range range) +{ + if (count + 1 > capacity) + expandSourceRanges(array, count, capacity); + + array[count] = range; + count++; +} + +static inline void expandReplacements(UString * & array, int& count, int& capacity) +{ + int newCapacity; + if (capacity == 0) { + newCapacity = 16; + } else { + newCapacity = capacity * 2; + } + + UString *newArray = new UString[newCapacity]; + for (int i = 0; i < count; i++) { + newArray[i] = array[i]; + } + + delete [] array; + + capacity = newCapacity; + array = newArray; +} + +static void pushReplacement(UString * & array, int& count, int& capacity, UString replacement) +{ + if (count + 1 > capacity) + expandReplacements(array, count, capacity); + + array[count] = replacement; + count++; +} + +static inline UString substituteBackreferences(const UString &replacement, const UString &source, int *ovector, RegExp *reg) +{ + UString substitutedReplacement = replacement; + + int i = -1; + while ((i = substitutedReplacement.find(UString("$"), i + 1)) != -1) { + if (i+1 == substitutedReplacement.size()) + break; + + unsigned short ref = substitutedReplacement[i+1].unicode(); + int backrefStart = 0; + int backrefLength = 0; + int advance = 0; + + if (ref == '$') { // "$$" -> "$" + substitutedReplacement = substitutedReplacement.substr(0, i + 1) + substitutedReplacement.substr(i + 2); + continue; + } else 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.size() - backrefStart; + } else if (ref >= '0' && ref <= '9') { + // 1- and 2-digit back references are allowed + unsigned backrefIndex = ref - '0'; + if (backrefIndex > reg->numSubpatterns()) + continue; + if (substitutedReplacement.size() > i + 2) { + ref = substitutedReplacement[i+2].unicode(); + 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; + } + } + backrefStart = ovector[2 * backrefIndex]; + backrefLength = ovector[2 * backrefIndex + 1] - backrefStart; + } else + continue; + + substitutedReplacement = substitutedReplacement.substr(0, i) + source.substr(backrefStart, backrefLength) + substitutedReplacement.substr(i + 2 + advance); + i += backrefLength - 1; // - 1 offsets 'i + 1' + } + + return substitutedReplacement; +} +static inline int localeCompare(const UString& a, const UString& b) +{ +#if PLATFORM(WIN_OS) + int retval = CompareStringW(LOCALE_USER_DEFAULT, 0, + reinterpret_cast<LPCWSTR>(a.data()), a.size(), + reinterpret_cast<LPCWSTR>(b.data()), b.size()); + return !retval ? retval : retval - 2; +#elif PLATFORM(CF) + CFStringRef sa = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(a.data()), a.size(), kCFAllocatorNull); + CFStringRef sb = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(b.data()), b.size(), kCFAllocatorNull); + + int retval = CFStringCompare(sa, sb, kCFCompareLocalized); + + CFRelease(sa); + CFRelease(sb); + + return retval; +#else + return compare(a, b); +#endif +} + +static JSValue *replace(ExecState *exec, StringImp* sourceVal, JSValue *pattern, JSValue *replacement) +{ + UString source = sourceVal->value(); + JSObject *replacementFunction = 0; + UString replacementString; + + if (replacement->isObject() && replacement->toObject(exec)->implementsCall()) + replacementFunction = replacement->toObject(exec); + else + replacementString = replacement->toString(exec); + + if (pattern->isObject() && static_cast<JSObject *>(pattern)->inherits(&RegExpImp::info)) { + RegExp *reg = static_cast<RegExpImp *>(pattern)->regExp(); + bool global = reg->global(); + + RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); + + int lastIndex = 0; + int startPosition = 0; + + UString::Range *sourceRanges = 0; + int sourceRangeCount = 0; + int sourceRangeCapacity = 0; + UString *replacements = 0; + int replacementCount = 0; + int replacementCapacity = 0; + + // This is either a loop (if global is set) or a one-way (if not). + do { + int matchIndex; + int matchLen; + int* ovector; + regExpObj->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); + if (matchIndex < 0) + break; + + pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, matchIndex - lastIndex)); + + UString substitutedReplacement; + if (replacementFunction) { + int completeMatchStart = ovector[0]; + List args; + + for (unsigned i = 0; i < reg->numSubpatterns() + 1; i++) { + int matchStart = ovector[i * 2]; + int matchLen = ovector[i * 2 + 1] - matchStart; + + if (matchStart < 0) + args.append(jsUndefined()); + else + args.append(jsString(source.substr(matchStart, matchLen))); + } + + args.append(jsNumber(completeMatchStart)); + args.append(sourceVal); + + substitutedReplacement = replacementFunction->call(exec, exec->dynamicGlobalObject(), + args)->toString(exec); + } else + substitutedReplacement = substituteBackreferences(replacementString, source, ovector, reg); + + pushReplacement(replacements, replacementCount, replacementCapacity, substitutedReplacement); + + lastIndex = matchIndex + matchLen; + startPosition = lastIndex; + + // special case of empty match + if (matchLen == 0) { + startPosition++; + if (startPosition > source.size()) + break; + } + } while (global); + + if (lastIndex < source.size()) + pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, source.size() - lastIndex)); + + UString result; + + if (sourceRanges) + result = source.spliceSubstringsWithSeparators(sourceRanges, sourceRangeCount, replacements, replacementCount); + + delete [] sourceRanges; + delete [] replacements; + + if (result == source) + return sourceVal; + + return jsString(result); + } + + // First arg is a string + UString patternString = pattern->toString(exec); + int matchPos = source.find(patternString); + int matchLen = patternString.size(); + // Do the replacement + if (matchPos == -1) + return sourceVal; + + if (replacementFunction) { + List args; + + args.append(jsString(source.substr(matchPos, matchLen))); + args.append(jsNumber(matchPos)); + args.append(sourceVal); + + replacementString = replacementFunction->call(exec, exec->dynamicGlobalObject(), + args)->toString(exec); + } + + return jsString(source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen)); +} + +JSValue* stringProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&StringInstance::info)) + return throwError(exec, TypeError); + + return static_cast<StringInstance*>(thisObj)->internalValue(); +} + +JSValue* stringProtoFuncValueOf(ExecState* exec, JSObject* thisObj, const List&) +{ + if (!thisObj->inherits(&StringInstance::info)) + return throwError(exec, TypeError); + + return static_cast<StringInstance*>(thisObj)->internalValue(); +} + +JSValue* stringProtoFuncCharAt(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + UString u; + JSValue* a0 = args[0]; + double dpos = a0->toInteger(exec); + if (dpos >= 0 && dpos < len) + u = s.substr(static_cast<int>(dpos), 1); + else + u = ""; + return jsString(u); +} + +JSValue* stringProtoFuncCharCodeAt(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + JSValue* result = 0; + + JSValue* a0 = args[0]; + double dpos = a0->toInteger(exec); + if (dpos >= 0 && dpos < len) + result = jsNumber(s[static_cast<int>(dpos)].unicode()); + else + result = jsNaN(); + return result; +} + +JSValue* stringProtoFuncConcat(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + List::const_iterator end = args.end(); + for (List::const_iterator it = args.begin(); it != end; ++it) { + s += (*it)->toString(exec); + } + return jsString(s); +} + +JSValue* stringProtoFuncIndexOf(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + JSValue* a0 = args[0]; + JSValue* a1 = args[1]; + UString u2 = a0->toString(exec); + double dpos = a1->toInteger(exec); + if (dpos < 0) + dpos = 0; + else if (dpos > len) + dpos = len; + return jsNumber(s.find(u2, static_cast<int>(dpos))); +} + +JSValue* stringProtoFuncLastIndexOf(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + JSValue* a0 = args[0]; + JSValue* a1 = args[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; + return jsNumber(s.rfind(u2, static_cast<int>(dpos))); +} + +JSValue* stringProtoFuncMatch(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + JSValue* a0 = args[0]; + + UString u = s; + RefPtr<RegExp> reg; + RegExpImp* imp = 0; + if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) { + reg = static_cast<RegExpImp *>(a0)->regExp(); + } else { + /* + * ECMA 15.5.4.12 String.prototype.search (regexp) + * If regexp is not an object whose [[Class]] property is "RegExp", it is + * replaced with the result of the expression new RegExp(regexp). + */ + reg = RegExp::create(a0->toString(exec)); + } + RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); + int pos; + int matchLength; + regExpObj->performMatch(reg.get(), u, 0, pos, matchLength); + JSValue* result; + if (!(reg->global())) { + // case without 'g' flag is handled like RegExp.prototype.exec + if (pos < 0) + result = jsNull(); + else + result = regExpObj->arrayOfMatches(exec); + } else { + // return array of matches + List list; + int lastIndex = 0; + while (pos >= 0) { + list.append(jsString(u.substr(pos, matchLength))); + lastIndex = pos; + pos += matchLength == 0 ? 1 : matchLength; + regExpObj->performMatch(reg.get(), u, pos, pos, matchLength); + } + if (imp) + imp->setLastIndex(lastIndex); + if (list.isEmpty()) { + // if there are no matches at all, it's important to return + // Null instead of an empty array, because this matches + // other browsers and because Null is a false value. + result = jsNull(); + } else { + result = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, list); + } + } + return result; +} + +JSValue* stringProtoFuncSearch(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + JSValue* a0 = args[0]; + + UString u = s; + RefPtr<RegExp> reg; + if (a0->isObject() && static_cast<JSObject*>(a0)->inherits(&RegExpImp::info)) { + reg = static_cast<RegExpImp*>(a0)->regExp(); + } else { + /* + * ECMA 15.5.4.12 String.prototype.search (regexp) + * If regexp is not an object whose [[Class]] property is "RegExp", it is + * replaced with the result of the expression new RegExp(regexp). + */ + reg = RegExp::create(a0->toString(exec)); + } + RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); + int pos; + int matchLength; + regExpObj->performMatch(reg.get(), u, 0, pos, matchLength); + return jsNumber(pos); +} + +JSValue* stringProtoFuncReplace(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + StringImp* sVal = thisObj->inherits(&StringInstance::info) ? + static_cast<StringInstance*>(thisObj)->internalValue() : + static_cast<StringImp*>(jsString(s)); + + JSValue* a0 = args[0]; + JSValue* a1 = args[1]; + + return replace(exec, sVal, a0, a1); +} + +JSValue* stringProtoFuncSlice(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + JSValue* a0 = args[0]; + JSValue* a1 = args[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 jsString(s.substr(static_cast<int>(from), static_cast<int>(to - from))); + } + + return jsString(""); +} + +JSValue* stringProtoFuncSplit(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + JSValue* a0 = args[0]; + JSValue* a1 = args[1]; + + JSObject *constructor = exec->lexicalGlobalObject()->arrayConstructor(); + JSObject* res = static_cast<JSObject*>(constructor->construct(exec, exec->emptyList())); + JSValue* result = res; + UString u = s; + int pos; + int i = 0; + int p0 = 0; + uint32_t limit = a1->isUndefined() ? 0xFFFFFFFFU : a1->toUInt32(exec); + if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) { + RegExp *reg = static_cast<RegExpImp *>(a0)->regExp(); + if (u.isEmpty() && reg->match(u, 0) >= 0) { + // empty string matched by regexp -> empty array + res->put(exec, exec->propertyNames().length, jsNumber(0)); + return result; + } + pos = 0; + while (static_cast<uint32_t>(i) != limit && pos < u.size()) { + OwnArrayPtr<int> ovector; + int mpos = reg->match(u, pos, &ovector); + if (mpos < 0) + break; + int mlen = ovector[1] - ovector[0]; + pos = mpos + (mlen == 0 ? 1 : mlen); + if (mpos != p0 || mlen) { + res->put(exec,i, jsString(u.substr(p0, mpos-p0))); + p0 = mpos + mlen; + i++; + } + for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) { + int spos = ovector[si * 2]; + if (spos < 0) + res->put(exec, i++, jsUndefined()); + else + res->put(exec, i++, jsString(u.substr(spos, ovector[si * 2 + 1] - spos))); + } + } + } else { + UString u2 = a0->toString(exec); + if (u2.isEmpty()) { + if (u.isEmpty()) { + // empty separator matches empty string -> empty array + res->put(exec, exec->propertyNames().length, jsNumber(0)); + return result; + } else { + while (static_cast<uint32_t>(i) != limit && i < u.size()-1) + res->put(exec, i++, jsString(u.substr(p0++, 1))); + } + } else { + while (static_cast<uint32_t>(i) != limit && (pos = u.find(u2, p0)) >= 0) { + res->put(exec, i, jsString(u.substr(p0, pos-p0))); + p0 = pos + u2.size(); + i++; + } + } + } + // add remaining string, if any + if (static_cast<uint32_t>(i) != limit) + res->put(exec, i++, jsString(u.substr(p0))); + res->put(exec, exec->propertyNames().length, jsNumber(i)); + return result; +} + +JSValue* stringProtoFuncSubstr(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + JSValue* a0 = args[0]; + JSValue* a1 = args[1]; + + double start = a0->toInteger(exec); + double length = a1->isUndefined() ? len : a1->toInteger(exec); + if (start >= len) + return jsString(""); + if (length < 0) + return jsString(""); + if (start < 0) { + start += len; + if (start < 0) + start = 0; + } + if (length > len) + length = len; + return jsString(s.substr(static_cast<int>(start), static_cast<int>(length))); +} + +JSValue* stringProtoFuncSubstring(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + int len = s.size(); + + JSValue* a0 = args[0]; + JSValue* a1 = args[1]; + + double start = a0->toNumber(exec); + double end = a1->toNumber(exec); + if (isnan(start)) + start = 0; + if (isnan(end)) + end = 0; + if (start < 0) + start = 0; + if (end < 0) + end = 0; + if (start > len) + start = len; + if (end > len) + end = len; + if (a1->isUndefined()) + end = len; + if (start > end) { + double temp = end; + end = start; + start = temp; + } + return jsString(s.substr((int)start, (int)end-(int)start)); +} + +JSValue* stringProtoFuncToLowerCase(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + StringImp* sVal = thisObj->inherits(&StringInstance::info) + ? static_cast<StringInstance*>(thisObj)->internalValue() + : static_cast<StringImp*>(jsString(s)); + int ssize = s.size(); + if (!ssize) + return sVal; + Vector< ::UChar> buffer(ssize); + bool error; + int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) { + buffer.resize(length); + length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) + return sVal; + } + if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) + return sVal; + return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); +} + +JSValue* stringProtoFuncToUpperCase(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + StringImp* sVal = thisObj->inherits(&StringInstance::info) + ? static_cast<StringInstance*>(thisObj)->internalValue() + : static_cast<StringImp*>(jsString(s)); + int ssize = s.size(); + if (!ssize) + return sVal; + Vector< ::UChar> buffer(ssize); + bool error; + int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) { + buffer.resize(length); + length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) + return sVal; + } + if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) + return sVal; + return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); +} + +JSValue* stringProtoFuncToLocaleLowerCase(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented. + StringImp* sVal = thisObj->inherits(&StringInstance::info) + ? static_cast<StringInstance*>(thisObj)->internalValue() + : static_cast<StringImp*>(jsString(s)); + int ssize = s.size(); + if (!ssize) + return sVal; + Vector< ::UChar> buffer(ssize); + bool error; + int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) { + buffer.resize(length); + length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) + return sVal; + } + if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) + return sVal; + return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); +} + +JSValue* stringProtoFuncToLocaleUpperCase(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + + StringImp* sVal = thisObj->inherits(&StringInstance::info) + ? static_cast<StringInstance*>(thisObj)->internalValue() + : static_cast<StringImp*>(jsString(s)); + int ssize = s.size(); + if (!ssize) + return sVal; + Vector< ::UChar> buffer(ssize); + bool error; + int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) { + buffer.resize(length); + length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const ::UChar*>(s.data()), ssize, &error); + if (error) + return sVal; + } + if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) + return sVal; + return jsString(UString(reinterpret_cast<UChar*>(buffer.releaseBuffer()), length, false)); +} + +JSValue* stringProtoFuncLocaleCompare(ExecState* exec, JSObject* thisObj, const List& args) +{ + if (args.size() < 1) + return jsNumber(0); + + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + JSValue* a0 = args[0]; + return jsNumber(localeCompare(s, a0->toString(exec))); +} + +JSValue* stringProtoFuncBig(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<big>" + s + "</big>"); +} + +JSValue* stringProtoFuncSmall(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<small>" + s + "</small>"); +} + +JSValue* stringProtoFuncBlink(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<blink>" + s + "</blink>"); +} + +JSValue* stringProtoFuncBold(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<b>" + s + "</b>"); +} + +JSValue* stringProtoFuncFixed(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<tt>" + s + "</tt>"); +} + +JSValue* stringProtoFuncItalics(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<i>" + s + "</i>"); +} + +JSValue* stringProtoFuncStrike(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<strike>" + s + "</strike>"); +} + +JSValue* stringProtoFuncSub(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<sub>" + s + "</sub>"); +} + +JSValue* stringProtoFuncSup(ExecState* exec, JSObject* thisObj, const List&) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + return jsString("<sup>" + s + "</sup>"); +} + +JSValue* stringProtoFuncFontcolor(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + JSValue* a0 = args[0]; + return jsString("<font color=\"" + a0->toString(exec) + "\">" + s + "</font>"); +} + +JSValue* stringProtoFuncFontsize(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + JSValue* a0 = args[0]; + return jsString("<font size=\"" + a0->toString(exec) + "\">" + s + "</font>"); +} + +JSValue* stringProtoFuncAnchor(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + JSValue* a0 = args[0]; + return jsString("<a name=\"" + a0->toString(exec) + "\">" + s + "</a>"); +} + +JSValue* stringProtoFuncLink(ExecState* exec, JSObject* thisObj, const List& args) +{ + // This optimizes the common case that thisObj is a StringInstance + UString s = thisObj->inherits(&StringInstance::info) ? static_cast<StringInstance*>(thisObj)->internalValue()->value() : thisObj->toString(exec); + JSValue* a0 = args[0]; + return jsString("<a href=\"" + a0->toString(exec) + "\">" + s + "</a>"); +} + +// ------------------------------ StringObjectImp ------------------------------ + +StringObjectImp::StringObjectImp(ExecState* exec, FunctionPrototype* funcProto, StringPrototype* stringProto) + : InternalFunctionImp(funcProto, stringProto->classInfo()->className) +{ + // ECMA 15.5.3.1 String.prototype + putDirect(exec->propertyNames().prototype, stringProto, DontEnum|DontDelete|ReadOnly); + + putDirectFunction(new StringObjectFuncImp(exec, funcProto, exec->propertyNames().fromCharCode), DontEnum); + + // no. of arguments for constructor + putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); +} + + +bool StringObjectImp::implementsConstruct() const +{ + return true; +} + +// ECMA 15.5.2 +JSObject *StringObjectImp::construct(ExecState *exec, const List &args) +{ + JSObject *proto = exec->lexicalGlobalObject()->stringPrototype(); + if (args.size() == 0) + return new StringInstance(proto); + return new StringInstance(proto, args[0]->toString(exec)); +} + +// ECMA 15.5.1 +JSValue *StringObjectImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args) +{ + if (args.isEmpty()) + return jsString(""); + else { + JSValue *v = args[0]; + return jsString(v->toString(exec)); + } +} + +// ------------------------------ StringObjectFuncImp -------------------------- + +// ECMA 15.5.3.2 fromCharCode() +StringObjectFuncImp::StringObjectFuncImp(ExecState* exec, FunctionPrototype* funcProto, const Identifier& name) + : InternalFunctionImp(funcProto, name) +{ + putDirect(exec->propertyNames().length, jsNumber(1), DontDelete|ReadOnly|DontEnum); +} + +JSValue *StringObjectFuncImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args) +{ + UString s; + if (args.size()) { + UChar *buf = static_cast<UChar *>(fastMalloc(args.size() * sizeof(UChar))); + UChar *p = buf; + List::const_iterator end = args.end(); + for (List::const_iterator it = args.begin(); it != end; ++it) { + unsigned short u = static_cast<unsigned short>((*it)->toUInt32(exec)); + *p++ = UChar(u); + } + s = UString(buf, args.size(), false); + } else + s = ""; + + return jsString(s); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/string_object.h b/JavaScriptCore/kjs/string_object.h new file mode 100644 index 0000000..8508788 --- /dev/null +++ b/JavaScriptCore/kjs/string_object.h @@ -0,0 +1,153 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef STRING_OBJECT_H_ +#define STRING_OBJECT_H_ + +#include "function_object.h" +#include "JSWrapperObject.h" +#include "internal.h" +#include "lookup.h" + +namespace KJS { + + class StringInstance : public JSWrapperObject { + public: + StringInstance(JSObject *proto); + StringInstance(JSObject *proto, StringImp*); + StringInstance(JSObject *proto, const UString&); + + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + + virtual void put(ExecState* exec, const Identifier& propertyName, JSValue*); + virtual bool deleteProperty(ExecState* exec, const Identifier& propertyName); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual const ClassInfo *classInfo() const { return &info; } + static const ClassInfo info; + + StringImp* internalValue() const { return static_cast<StringImp*>(JSWrapperObject::internalValue());} + + private: + bool inlineGetOwnPropertySlot(ExecState*, unsigned, PropertySlot&); + + static JSValue* lengthGetter(ExecState*, JSObject *, const Identifier&, const PropertySlot&); + static JSValue* indexGetter(ExecState*, JSObject *, const Identifier&, const PropertySlot&); + }; + + // WebCore uses this to make style.filter undetectable + class StringInstanceThatMasqueradesAsUndefined : public StringInstance { + public: + StringInstanceThatMasqueradesAsUndefined(JSObject* proto, const UString& string) + : StringInstance(proto, string) { } + virtual bool masqueradeAsUndefined() const { return true; } + virtual bool toBoolean(ExecState*) const { return false; } + }; + + /** + * @internal + * + * The initial value of String.prototype (and thus all objects created + * with the String constructor + */ + class StringPrototype : public StringInstance { + public: + StringPrototype(ExecState *exec, + ObjectPrototype *objProto); + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual const ClassInfo *classInfo() const { return &info; } + static const ClassInfo info; + }; + + /** + * @internal + * + * Functions to implement all methods that are properties of the + * String.prototype object + */ + + JSValue* stringProtoFuncToString(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncValueOf(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncCharAt(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncCharCodeAt(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncConcat(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncIndexOf(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncLastIndexOf(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncMatch(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncReplace(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSearch(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSlice(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSplit(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSubstr(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSubstring(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncToLowerCase(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncToUpperCase(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncToLocaleLowerCase(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncToLocaleUpperCase(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncLocaleCompare(ExecState*, JSObject*, const List&); + + JSValue* stringProtoFuncBig(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSmall(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncBlink(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncBold(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncFixed(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncItalics(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncStrike(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSub(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncSup(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncFontcolor(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncFontsize(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncAnchor(ExecState*, JSObject*, const List&); + JSValue* stringProtoFuncLink(ExecState*, JSObject*, const List&); + + /** + * @internal + * + * The initial value of the the global variable's "String" property + */ + class StringObjectImp : public InternalFunctionImp { + public: + StringObjectImp(ExecState *exec, + FunctionPrototype *funcProto, + StringPrototype *stringProto); + + virtual bool implementsConstruct() const; + virtual JSObject *construct(ExecState *exec, const List &args); + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + }; + + /** + * @internal + * + * Class to implement all methods that are properties of the + * String object + */ + class StringObjectFuncImp : public InternalFunctionImp { + public: + StringObjectFuncImp(ExecState*, FunctionPrototype*, const Identifier&); + virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args); + }; + +} // namespace + +#endif + diff --git a/JavaScriptCore/kjs/testkjs.cpp b/JavaScriptCore/kjs/testkjs.cpp new file mode 100644 index 0000000..c229336 --- /dev/null +++ b/JavaScriptCore/kjs/testkjs.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com) + * + * 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 "JSGlobalObject.h" +#include "JSLock.h" +#include "Parser.h" +#include "array_object.h" +#include "collector.h" +#include "function.h" +#include "interpreter.h" +#include "nodes.h" +#include "object.h" +#include "protect.h" +#include <math.h> +#include <stdio.h> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/HashTraits.h> + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if PLATFORM(WIN_OS) +#include <crtdbg.h> +#include <windows.h> +#endif + +#if PLATFORM(QT) +#include <QDateTime> +#endif + +using namespace KJS; +using namespace WTF; + +static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer); + +static JSValue* functionPrint(ExecState*, JSObject*, const List&); +static JSValue* functionDebug(ExecState*, JSObject*, const List&); +static JSValue* functionGC(ExecState*, JSObject*, const List&); +static JSValue* functionVersion(ExecState*, JSObject*, const List&); +static JSValue* functionRun(ExecState*, JSObject*, const List&); +static JSValue* functionLoad(ExecState*, JSObject*, const List&); +static JSValue* functionReadline(ExecState*, JSObject*, const List&); +static JSValue* functionQuit(ExecState*, JSObject*, const List&); + +class StopWatch { +public: + void start(); + void stop(); + long getElapsedMS(); // call stop() first + +private: +#if PLATFORM(QT) + uint m_startTime; + uint m_stopTime; +#elif PLATFORM(WIN_OS) + DWORD m_startTime; + DWORD m_stopTime; +#else + // Windows does not have timeval, disabling this class for now (bug 7399) + timeval m_startTime; + timeval m_stopTime; +#endif +}; + +void StopWatch::start() +{ +#if PLATFORM(QT) + QDateTime t = QDateTime::currentDateTime(); + m_startTime = t.toTime_t() * 1000 + t.time().msec(); +#elif PLATFORM(WIN_OS) + m_startTime = timeGetTime(); +#else + gettimeofday(&m_startTime, 0); +#endif +} + +void StopWatch::stop() +{ +#if PLATFORM(QT) + QDateTime t = QDateTime::currentDateTime(); + m_stopTime = t.toTime_t() * 1000 + t.time().msec(); +#elif PLATFORM(WIN_OS) + m_stopTime = timeGetTime(); +#else + gettimeofday(&m_stopTime, 0); +#endif +} + +long StopWatch::getElapsedMS() +{ +#if PLATFORM(WIN_OS) || PLATFORM(QT) + return m_stopTime - m_startTime; +#else + timeval elapsedTime; + timersub(&m_stopTime, &m_startTime, &elapsedTime); + + return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f); +#endif +} + +class GlobalObject : public JSGlobalObject { +public: + GlobalObject(Vector<UString>& arguments); + virtual UString className() const { return "global"; } +}; +COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false); + +GlobalObject::GlobalObject(Vector<UString>& arguments) +{ + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "debug", functionDebug)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "print", functionPrint)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 0, "quit", functionQuit)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 0, "gc", functionGC)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "version", functionVersion)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "run", functionRun)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "load", functionLoad)); + putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 0, "readline", functionReadline)); + + JSObject* array = arrayConstructor()->construct(globalExec(), globalExec()->emptyList()); + for (size_t i = 0; i < arguments.size(); ++i) + array->put(globalExec(), i, jsString(arguments[i])); + putDirect("arguments", array); + + Interpreter::setShouldPrintExceptions(true); +} + +JSValue* functionPrint(ExecState* exec, JSObject*, const List& args) +{ + printf("%s\n", args[0]->toString(exec).UTF8String().c_str()); + return jsUndefined(); +} + +JSValue* functionDebug(ExecState* exec, JSObject*, const List& args) +{ + fprintf(stderr, "--> %s\n", args[0]->toString(exec).UTF8String().c_str()); + return jsUndefined(); +} + +JSValue* functionGC(ExecState*, JSObject*, const List&) +{ + JSLock lock; + Collector::collect(); + return jsUndefined(); +} + +JSValue* functionVersion(ExecState*, JSObject*, const List&) +{ + // We need this function for compatibility with the Mozilla JS tests but for now + // we don't actually do any version-specific handling + return jsUndefined(); +} + +JSValue* functionRun(ExecState* exec, JSObject*, const List& args) +{ + StopWatch stopWatch; + UString fileName = args[0]->toString(exec); + Vector<char> script; + if (!fillBufferWithContentsOfFile(fileName, script)) + return throwError(exec, GeneralError, "Could not open file."); + + stopWatch.start(); + Interpreter::evaluate(exec->dynamicGlobalObject()->globalExec(), fileName, 0, script.data()); + stopWatch.stop(); + + return jsNumber(stopWatch.getElapsedMS()); +} + +JSValue* functionLoad(ExecState* exec, JSObject*, const List& args) +{ + UString fileName = args[0]->toString(exec); + Vector<char> script; + if (!fillBufferWithContentsOfFile(fileName, script)) + return throwError(exec, GeneralError, "Could not open file."); + + Interpreter::evaluate(exec->dynamicGlobalObject()->globalExec(), fileName, 0, script.data()); + + return jsUndefined(); +} + +JSValue* functionReadline(ExecState*, JSObject*, const List&) +{ + Vector<char, 256> line; + int c; + while ((c = getchar()) != EOF) { + // FIXME: Should we also break on \r? + if (c == '\n') + break; + line.append(c); + } + line.append('\0'); + return jsString(line.data()); +} + +JSValue* functionQuit(ExecState*, JSObject*, const List&) +{ + exit(0); +#if !COMPILER(MSVC) + // MSVC knows that exit(0) never returns, so it flags this return statement as unreachable. + return jsUndefined(); +#endif +} + +// Use SEH for Release builds only to get rid of the crash report dialog +// (luckily the same tests fail in Release and Debug builds so far). Need to +// be in a separate main function because the kjsmain function requires object +// unwinding. + +#if PLATFORM(WIN_OS) && !defined(_DEBUG) +#define TRY __try { +#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; } +#else +#define TRY +#define EXCEPT(x) +#endif + +int kjsmain(int argc, char** argv); + +int main(int argc, char** argv) +{ +#if defined(_DEBUG) && PLATFORM(WIN_OS) + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); +#endif + + int res = 0; + TRY + res = kjsmain(argc, argv); + EXCEPT(res = 3) + return res; +} + +static bool prettyPrintScript(const UString& fileName, const Vector<char>& script) +{ + int errLine = 0; + UString errMsg; + UString scriptUString(script.data()); + RefPtr<ProgramNode> programNode = parser().parse<ProgramNode>(fileName, 0, scriptUString.data(), scriptUString.size(), 0, &errLine, &errMsg); + if (!programNode) { + fprintf(stderr, "%s:%d: %s.\n", fileName.UTF8String().c_str(), errLine, errMsg.UTF8String().c_str()); + return false; + } + + printf("%s\n", programNode->toString().UTF8String().c_str()); + return true; +} + +static bool runWithScripts(const Vector<UString>& fileNames, Vector<UString>& arguments, bool prettyPrint) +{ + GlobalObject* globalObject = new GlobalObject(arguments); + Vector<char> script; + + bool success = true; + + for (size_t i = 0; i < fileNames.size(); i++) { + UString fileName = fileNames[i]; + + if (!fillBufferWithContentsOfFile(fileName, script)) + return false; // fail early so we can catch missing files + + if (prettyPrint) + prettyPrintScript(fileName, script); + else { + Completion completion = Interpreter::evaluate(globalObject->globalExec(), fileName, 0, script.data()); + success = success && completion.complType() != Throw; + } + } + return success; +} + +static void printUsageStatement() +{ + fprintf(stderr, "Usage: testkjs -f file1 [-f file2...][-p][-- arguments...]\n"); + exit(-1); +} + +static void parseArguments(int argc, char** argv, Vector<UString>& fileNames, Vector<UString>& arguments, bool& prettyPrint) +{ + if (argc < 3) + printUsageStatement(); + + int i = 1; + for (; i < argc; ++i) { + const char* arg = argv[i]; + if (strcmp(arg, "-f") == 0) { + if (++i == argc) + printUsageStatement(); + fileNames.append(argv[i]); + continue; + } + if (strcmp(arg, "-p") == 0) { + prettyPrint = true; + continue; + } + if (strcmp(arg, "--") == 0) { + ++i; + break; + } + break; + } + + for (; i < argc; ++i) + arguments.append(argv[i]); +} + +int kjsmain(int argc, char** argv) +{ + JSLock lock; + + bool prettyPrint = false; + Vector<UString> fileNames; + Vector<UString> arguments; + parseArguments(argc, argv, fileNames, arguments, prettyPrint); + + bool success = runWithScripts(fileNames, arguments, prettyPrint); + +#ifndef NDEBUG + Collector::collect(); +#endif + + return success ? 0 : 3; +} + +static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer) +{ + FILE* f = fopen(fileName.UTF8String().c_str(), "r"); + if (!f) { + fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str()); + return false; + } + + size_t buffer_size = 0; + size_t buffer_capacity = 1024; + + buffer.resize(buffer_capacity); + + while (!feof(f) && !ferror(f)) { + buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f); + if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0' + buffer_capacity *= 2; + buffer.resize(buffer_capacity); + } + } + fclose(f); + buffer[buffer_size] = '\0'; + + return true; +} diff --git a/JavaScriptCore/kjs/testkjs.pro b/JavaScriptCore/kjs/testkjs.pro new file mode 100644 index 0000000..5a980eb --- /dev/null +++ b/JavaScriptCore/kjs/testkjs.pro @@ -0,0 +1,38 @@ +TEMPLATE = app +TARGET = testkjs +DESTDIR = .. +SOURCES = testkjs.cpp +QT -= gui +DEFINES -= KJS_IDENTIFIER_HIDE_GLOBALS +INCLUDEPATH += $$PWD/.. $$PWD $$PWD/../bindings $$PWD/../bindings/c $$PWD/../wtf +CONFIG -= app_bundle +qt-port:DEFINES += BUILDING_QT__ +#qt-port:LIBS += -L$$OUTPUT_DIR/lib -lQtWebKit + +CONFIG += link_pkgconfig +gtk-port:PKGCONFIG += glib-2.0 gobject-2.0 gthread-2.0 + +gtk-port { + QMAKE_CXXFLAGS += $$system(icu-config --cppflags) + LIBS += $$system(icu-config --ldflags) +} +QMAKE_RPATHDIR += $$OUTPUT_DIR/lib + +isEmpty(OUTPUT_DIR):OUTPUT_DIR=$$PWD/../.. +include($$OUTPUT_DIR/config.pri) +OBJECTS_DIR = tmp +OBJECTS_DIR_WTR = $$OBJECTS_DIR/ +win32-*: OBJECTS_DIR_WTR ~= s|/|\| +include($$PWD/../JavaScriptCore.pri) + +# Hack! Fix this. +SOURCES -= API/JSBase.cpp \ + API/JSCallbackConstructor.cpp \ + API/JSCallbackFunction.cpp \ + API/JSCallbackObject.cpp \ + API/JSClassRef.cpp \ + API/JSContextRef.cpp \ + API/JSObjectRef.cpp \ + API/JSStringRef.cpp \ + API/JSValueRef.cpp + diff --git a/JavaScriptCore/kjs/types.h b/JavaScriptCore/kjs/types.h new file mode 100644 index 0000000..603b2a2 --- /dev/null +++ b/JavaScriptCore/kjs/types.h @@ -0,0 +1,25 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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 "completion.h" +#include "list.h" diff --git a/JavaScriptCore/kjs/ustring.cpp b/JavaScriptCore/kjs/ustring.cpp new file mode 100644 index 0000000..b658a8d --- /dev/null +++ b/JavaScriptCore/kjs/ustring.cpp @@ -0,0 +1,1285 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * + * 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 "JSLock.h" +#include "collector.h" +#include "dtoa.h" +#include "function.h" +#include "identifier.h" +#include "operations.h" +#include <ctype.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <wtf/Assertions.h> +#include <wtf/ASCIICType.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> +#include <wtf/unicode/UTF8.h> + +#if HAVE(STRING_H) +#include <string.h> +#endif +#if HAVE(STRINGS_H) +#include <strings.h> +#endif + +using namespace WTF; +using namespace WTF::Unicode; +using namespace std; + +namespace KJS { + +extern const double NaN; +extern const double Inf; + +static inline const size_t overflowIndicator() { return std::numeric_limits<size_t>::max(); } +static inline const size_t maxUChars() { return std::numeric_limits<size_t>::max() / sizeof(UChar); } + +static inline UChar* allocChars(size_t length) +{ + ASSERT(length); + if (length > maxUChars()) + return 0; + return static_cast<UChar*>(fastMalloc(sizeof(UChar) * length)); +} + +static inline UChar* reallocChars(UChar* buffer, size_t length) +{ + ASSERT(length); + if (length > maxUChars()) + return 0; + return static_cast<UChar*>(fastRealloc(buffer, sizeof(UChar) * length)); +} + +COMPILE_ASSERT(sizeof(UChar) == 2, uchar_is_2_bytes) + +CString::CString(const char *c) +{ + length = strlen(c); + data = new char[length+1]; + memcpy(data, c, length + 1); +} + +CString::CString(const char *c, size_t len) +{ + length = len; + data = new char[len+1]; + memcpy(data, c, len); + data[len] = 0; +} + +CString::CString(const CString &b) +{ + length = b.length; + if (b.data) { + data = new char[length+1]; + memcpy(data, b.data, length + 1); + } + else + data = 0; +} + +CString::~CString() +{ + delete [] data; +} + +CString &CString::append(const CString &t) +{ + char *n; + n = new char[length+t.length+1]; + if (length) + memcpy(n, data, length); + if (t.length) + memcpy(n+length, t.data, t.length); + length += t.length; + n[length] = 0; + + delete [] data; + data = n; + + return *this; +} + +CString &CString::operator=(const char *c) +{ + if (data) + delete [] data; + length = strlen(c); + data = new char[length+1]; + memcpy(data, c, length + 1); + + return *this; +} + +CString &CString::operator=(const CString &str) +{ + if (this == &str) + return *this; + + if (data) + delete [] data; + length = str.length; + if (str.data) { + data = new char[length + 1]; + memcpy(data, str.data, length + 1); + } + else + data = 0; + + return *this; +} + +bool operator==(const CString& c1, const CString& c2) +{ + size_t len = c1.size(); + return len == c2.size() && (len == 0 || memcmp(c1.c_str(), c2.c_str(), len) == 0); +} + +// Hack here to avoid a global with a constructor; point to an unsigned short instead of a UChar. +static unsigned short almostUChar; +UString::Rep UString::Rep::null = { 0, 0, 1, 0, 0, &UString::Rep::null, 0, 0, 0, 0, 0, 0 }; +UString::Rep UString::Rep::empty = { 0, 0, 1, 0, 0, &UString::Rep::empty, 0, reinterpret_cast<UChar*>(&almostUChar), 0, 0, 0, 0 }; +const int normalStatBufferSize = 4096; +static char *statBuffer = 0; // FIXME: This buffer is never deallocated. +static int statBufferSize = 0; + +PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar *d, int l) +{ + ASSERT(JSLock::lockCount() > 0); + + int sizeInBytes = l * sizeof(UChar); + UChar *copyD = static_cast<UChar *>(fastMalloc(sizeInBytes)); + memcpy(copyD, d, sizeInBytes); + + return create(copyD, l); +} + +PassRefPtr<UString::Rep> UString::Rep::create(UChar *d, int l) +{ + ASSERT(JSLock::lockCount() > 0); + + Rep* r = new Rep; + r->offset = 0; + r->len = l; + r->rc = 1; + r->_hash = 0; + r->isIdentifier = 0; + r->baseString = r; + r->reportedCost = 0; + r->buf = d; + r->usedCapacity = l; + r->capacity = l; + r->usedPreCapacity = 0; + r->preCapacity = 0; + + // steal the single reference this Rep was created with + return adoptRef(r); +} + +PassRefPtr<UString::Rep> UString::Rep::create(PassRefPtr<Rep> base, int offset, int length) +{ + ASSERT(JSLock::lockCount() > 0); + ASSERT(base); + + int baseOffset = base->offset; + + base = base->baseString; + + ASSERT(-(offset + baseOffset) <= base->usedPreCapacity); + ASSERT(offset + baseOffset + length <= base->usedCapacity); + + Rep *r = new Rep; + r->offset = baseOffset + offset; + r->len = length; + r->rc = 1; + r->_hash = 0; + r->isIdentifier = 0; + r->baseString = base.releaseRef(); + r->reportedCost = 0; + r->buf = 0; + r->usedCapacity = 0; + r->capacity = 0; + r->usedPreCapacity = 0; + r->preCapacity = 0; + + // steal the single reference this Rep was created with + return adoptRef(r); +} + +void UString::Rep::destroy() +{ + ASSERT(JSLock::lockCount() > 0); + + if (isIdentifier) + Identifier::remove(this); + if (baseString != this) { + baseString->deref(); + } else { + fastFree(buf); + } + delete this; +} + +// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's +// or anything like that. +const unsigned PHI = 0x9e3779b9U; + +// Paul Hsieh's SuperFastHash +// http://www.azillionmonkeys.com/qed/hash.html +unsigned UString::Rep::computeHash(const UChar *s, int len) +{ + unsigned l = len; + uint32_t hash = PHI; + uint32_t tmp; + + int rem = l & 1; + l >>= 1; + + // Main loop + for (; l > 0; l--) { + hash += s[0].uc; + tmp = (s[1].uc << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } + + // Handle end case + if (rem) { + hash += s[0].uc; + hash ^= hash << 11; + hash += hash >> 17; + } + + // Force "avalanching" of final 127 bits + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 2; + hash += hash >> 15; + hash ^= hash << 10; + + // this avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet", using a value that is likely to be + // effectively the same as 0 when the low bits are masked + if (hash == 0) + hash = 0x80000000; + + return hash; +} + +// Paul Hsieh's SuperFastHash +// http://www.azillionmonkeys.com/qed/hash.html +unsigned UString::Rep::computeHash(const char *s) +{ + // This hash is designed to work on 16-bit chunks at a time. But since the normal case + // (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they + // were 16-bit chunks, which should give matching results + + uint32_t hash = PHI; + uint32_t tmp; + size_t l = strlen(s); + + size_t rem = l & 1; + l >>= 1; + + // Main loop + for (; l > 0; l--) { + hash += (unsigned char)s[0]; + tmp = ((unsigned char)s[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } + + // Handle end case + if (rem) { + hash += (unsigned char)s[0]; + hash ^= hash << 11; + hash += hash >> 17; + } + + // Force "avalanching" of final 127 bits + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 2; + hash += hash >> 15; + hash ^= hash << 10; + + // this avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet", using a value that is likely to be + // effectively the same as 0 when the low bits are masked + if (hash == 0) + hash = 0x80000000; + + return hash; +} + +// put these early so they can be inlined +inline size_t UString::expandedSize(size_t size, size_t otherSize) const +{ + // Do the size calculation in two parts, returning overflowIndicator if + // we overflow the maximum value that we can handle. + + if (size > maxUChars()) + return overflowIndicator(); + + size_t expandedSize = ((size + 10) / 10 * 11) + 1; + if (maxUChars() - expandedSize < otherSize) + return overflowIndicator(); + + return expandedSize + otherSize; +} + +inline int UString::usedCapacity() const +{ + return m_rep->baseString->usedCapacity; +} + +inline int UString::usedPreCapacity() const +{ + return m_rep->baseString->usedPreCapacity; +} + +void UString::expandCapacity(int requiredLength) +{ + Rep* r = m_rep->baseString; + + if (requiredLength > r->capacity) { + size_t newCapacity = expandedSize(requiredLength, r->preCapacity); + UChar* oldBuf = r->buf; + r->buf = reallocChars(r->buf, newCapacity); + if (!r->buf) { + r->buf = oldBuf; + m_rep = &Rep::null; + return; + } + r->capacity = newCapacity - r->preCapacity; + } + if (requiredLength > r->usedCapacity) { + r->usedCapacity = requiredLength; + } +} + +void UString::expandPreCapacity(int requiredPreCap) +{ + Rep* r = m_rep->baseString; + + if (requiredPreCap > r->preCapacity) { + size_t newCapacity = expandedSize(requiredPreCap, r->capacity); + int delta = newCapacity - r->capacity - r->preCapacity; + + UChar* newBuf = allocChars(newCapacity); + if (!newBuf) { + m_rep = &Rep::null; + return; + } + memcpy(newBuf + delta, r->buf, (r->capacity + r->preCapacity) * sizeof(UChar)); + fastFree(r->buf); + r->buf = newBuf; + + r->preCapacity = newCapacity - r->capacity; + } + if (requiredPreCap > r->usedPreCapacity) { + r->usedPreCapacity = requiredPreCap; + } +} + +UString::UString(const char *c) +{ + if (!c) { + m_rep = &Rep::null; + return; + } + + if (!c[0]) { + m_rep = &Rep::empty; + return; + } + + size_t length = strlen(c); + UChar *d = allocChars(length); + if (!d) + m_rep = &Rep::null; + else { + for (size_t i = 0; i < length; i++) + d[i].uc = c[i]; + m_rep = Rep::create(d, static_cast<int>(length)); + } +} + +UString::UString(const UChar *c, int length) +{ + if (length == 0) + m_rep = &Rep::empty; + else + m_rep = Rep::createCopying(c, length); +} + +UString::UString(UChar *c, int length, bool copy) +{ + if (length == 0) + m_rep = &Rep::empty; + else if (copy) + m_rep = Rep::createCopying(c, length); + else + m_rep = Rep::create(c, length); +} + +UString::UString(const Vector<UChar>& buffer) +{ + if (!buffer.size()) + m_rep = &Rep::empty; + else + m_rep = Rep::createCopying(buffer.data(), buffer.size()); +} + + +UString::UString(const UString &a, const UString &b) +{ + int aSize = a.size(); + int aOffset = a.m_rep->offset; + int bSize = b.size(); + int bOffset = b.m_rep->offset; + int length = aSize + bSize; + + // possible cases: + + if (aSize == 0) { + // a is empty + m_rep = b.m_rep; + } else if (bSize == 0) { + // b is empty + m_rep = a.m_rep; + } else if (aOffset + aSize == a.usedCapacity() && aSize >= minShareSize && 4 * aSize >= bSize && + (-bOffset != b.usedPreCapacity() || aSize >= bSize)) { + // - a reaches the end of its buffer so it qualifies for shared append + // - also, it's at least a quarter the length of b - appending to a much shorter + // string does more harm than good + // - however, if b qualifies for prepend and is longer than a, we'd rather prepend + UString x(a); + x.expandCapacity(aOffset + length); + if (a.data() && x.data()) { + memcpy(const_cast<UChar *>(a.data() + aSize), b.data(), bSize * sizeof(UChar)); + m_rep = Rep::create(a.m_rep, 0, length); + } else + m_rep = &Rep::null; + } else if (-bOffset == b.usedPreCapacity() && bSize >= minShareSize && 4 * bSize >= aSize) { + // - b reaches the beginning of its buffer so it qualifies for shared prepend + // - also, it's at least a quarter the length of a - prepending to a much shorter + // string does more harm than good + UString y(b); + y.expandPreCapacity(-bOffset + aSize); + if (b.data() && y.data()) { + memcpy(const_cast<UChar *>(b.data() - aSize), a.data(), aSize * sizeof(UChar)); + m_rep = Rep::create(b.m_rep, -aSize, length); + } else + m_rep = &Rep::null; + } else { + // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string + size_t newCapacity = expandedSize(length, 0); + UChar* d = allocChars(newCapacity); + if (!d) + m_rep = &Rep::null; + else { + memcpy(d, a.data(), aSize * sizeof(UChar)); + memcpy(d + aSize, b.data(), bSize * sizeof(UChar)); + m_rep = Rep::create(d, length); + m_rep->capacity = newCapacity; + } + } +} + +const UString& UString::null() +{ + static UString* n = new UString; + return *n; +} + +UString UString::from(int i) +{ + UChar buf[1 + sizeof(i) * 3]; + UChar *end = buf + sizeof(buf) / sizeof(UChar); + UChar *p = end; + + if (i == 0) { + *--p = '0'; + } else if (i == INT_MIN) { + char minBuf[1 + sizeof(i) * 3]; + sprintf(minBuf, "%d", INT_MIN); + return UString(minBuf); + } else { + bool negative = false; + if (i < 0) { + negative = true; + i = -i; + } + while (i) { + *--p = (unsigned short)((i % 10) + '0'); + i /= 10; + } + if (negative) { + *--p = '-'; + } + } + + return UString(p, static_cast<int>(end - p)); +} + +UString UString::from(unsigned int u) +{ + UChar buf[sizeof(u) * 3]; + UChar *end = buf + sizeof(buf) / sizeof(UChar); + UChar *p = end; + + if (u == 0) { + *--p = '0'; + } else { + while (u) { + *--p = (unsigned short)((u % 10) + '0'); + u /= 10; + } + } + + return UString(p, static_cast<int>(end - p)); +} + +UString UString::from(long l) +{ + UChar buf[1 + sizeof(l) * 3]; + UChar *end = buf + sizeof(buf) / sizeof(UChar); + UChar *p = end; + + if (l == 0) { + *--p = '0'; + } else if (l == LONG_MIN) { + char minBuf[1 + sizeof(l) * 3]; + sprintf(minBuf, "%ld", LONG_MIN); + return UString(minBuf); + } else { + bool negative = false; + if (l < 0) { + negative = true; + l = -l; + } + while (l) { + *--p = (unsigned short)((l % 10) + '0'); + l /= 10; + } + if (negative) { + *--p = '-'; + } + } + + return UString(p, static_cast<int>(end - p)); +} + +UString UString::from(double d) +{ + // avoid ever printing -NaN, in JS conceptually there is only one NaN value + if (isnan(d)) + return "NaN"; + + char buf[80]; + int decimalPoint; + int sign; + + char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL); + int length = static_cast<int>(strlen(result)); + + int i = 0; + if (sign) { + buf[i++] = '-'; + } + + if (decimalPoint <= 0 && decimalPoint > -6) { + buf[i++] = '0'; + buf[i++] = '.'; + for (int j = decimalPoint; j < 0; j++) { + buf[i++] = '0'; + } + strcpy(buf + i, result); + } else if (decimalPoint <= 21 && decimalPoint > 0) { + if (length <= decimalPoint) { + strcpy(buf + i, result); + i += length; + for (int j = 0; j < decimalPoint - length; j++) { + buf[i++] = '0'; + } + buf[i] = '\0'; + } else { + strncpy(buf + i, result, decimalPoint); + i += decimalPoint; + buf[i++] = '.'; + strcpy(buf + i, result + decimalPoint); + } + } else if (result[0] < '0' || result[0] > '9') { + strcpy(buf + i, result); + } else { + buf[i++] = result[0]; + if (length > 1) { + buf[i++] = '.'; + strcpy(buf + i, result + 1); + i += length - 1; + } + + buf[i++] = 'e'; + buf[i++] = (decimalPoint >= 0) ? '+' : '-'; + // decimalPoint can't be more than 3 digits decimal given the + // nature of float representation + int exponential = decimalPoint - 1; + if (exponential < 0) + exponential = -exponential; + if (exponential >= 100) + buf[i++] = static_cast<char>('0' + exponential / 100); + if (exponential >= 10) + buf[i++] = static_cast<char>('0' + (exponential % 100) / 10); + buf[i++] = static_cast<char>('0' + exponential % 10); + buf[i++] = '\0'; + } + + kjs_freedtoa(result); + + return UString(buf); +} + +UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const +{ + if (rangeCount == 1 && separatorCount == 0) { + int thisSize = size(); + int position = substringRanges[0].position; + int length = substringRanges[0].length; + if (position <= 0 && length >= thisSize) + return *this; + return UString::Rep::create(m_rep, max(0, position), min(thisSize, length)); + } + + int totalLength = 0; + for (int i = 0; i < rangeCount; i++) + totalLength += substringRanges[i].length; + for (int i = 0; i < separatorCount; i++) + totalLength += separators[i].size(); + + if (totalLength == 0) + return ""; + + UChar* buffer = allocChars(totalLength); + if (!buffer) + return null(); + + int maxCount = max(rangeCount, separatorCount); + int bufferPos = 0; + for (int i = 0; i < maxCount; i++) { + if (i < rangeCount) { + memcpy(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length * sizeof(UChar)); + bufferPos += substringRanges[i].length; + } + if (i < separatorCount) { + memcpy(buffer + bufferPos, separators[i].data(), separators[i].size() * sizeof(UChar)); + bufferPos += separators[i].size(); + } + } + + return UString::Rep::create(buffer, totalLength); +} + +UString &UString::append(const UString &t) +{ + int thisSize = size(); + int thisOffset = m_rep->offset; + int tSize = t.size(); + int length = thisSize + tSize; + + // possible cases: + if (thisSize == 0) { + // this is empty + *this = t; + } else if (tSize == 0) { + // t is empty + } else if (m_rep->baseIsSelf() && m_rep->rc == 1) { + // this is direct and has refcount of 1 (so we can just alter it directly) + expandCapacity(thisOffset + length); + if (data()) { + memcpy(const_cast<UChar*>(data() + thisSize), t.data(), tSize * sizeof(UChar)); + m_rep->len = length; + m_rep->_hash = 0; + } + } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) { + // this reaches the end of the buffer - extend it if it's long enough to append to + expandCapacity(thisOffset + length); + if (data()) { + memcpy(const_cast<UChar*>(data() + thisSize), t.data(), tSize * sizeof(UChar)); + m_rep = Rep::create(m_rep, 0, length); + } + } else { + // this is shared with someone using more capacity, gotta make a whole new string + size_t newCapacity = expandedSize(length, 0); + UChar* d = allocChars(newCapacity); + if (!d) + m_rep = &Rep::null; + else { + memcpy(d, data(), thisSize * sizeof(UChar)); + memcpy(const_cast<UChar*>(d + thisSize), t.data(), tSize * sizeof(UChar)); + m_rep = Rep::create(d, length); + m_rep->capacity = newCapacity; + } + } + + return *this; +} + +UString &UString::append(const char *t) +{ + int thisSize = size(); + int thisOffset = m_rep->offset; + int tSize = static_cast<int>(strlen(t)); + int length = thisSize + tSize; + + // possible cases: + if (thisSize == 0) { + // this is empty + *this = t; + } else if (tSize == 0) { + // t is empty, we'll just return *this below. + } else if (m_rep->baseIsSelf() && m_rep->rc == 1) { + // this is direct and has refcount of 1 (so we can just alter it directly) + expandCapacity(thisOffset + length); + UChar *d = const_cast<UChar *>(data()); + if (d) { + for (int i = 0; i < tSize; ++i) + d[thisSize + i] = t[i]; + m_rep->len = length; + m_rep->_hash = 0; + } + } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) { + // this string reaches the end of the buffer - extend it + expandCapacity(thisOffset + length); + UChar *d = const_cast<UChar *>(data()); + if (d) { + for (int i = 0; i < tSize; ++i) + d[thisSize + i] = t[i]; + m_rep = Rep::create(m_rep, 0, length); + } + } else { + // this is shared with someone using more capacity, gotta make a whole new string + size_t newCapacity = expandedSize(length, 0); + UChar* d = allocChars(newCapacity); + if (!d) + m_rep = &Rep::null; + else { + memcpy(d, data(), thisSize * sizeof(UChar)); + for (int i = 0; i < tSize; ++i) + d[thisSize + i] = t[i]; + m_rep = Rep::create(d, length); + m_rep->capacity = newCapacity; + } + } + + return *this; +} + +UString &UString::append(unsigned short c) +{ + int thisOffset = m_rep->offset; + int length = size(); + + // possible cases: + if (length == 0) { + // this is empty - must make a new m_rep because we don't want to pollute the shared empty one + size_t newCapacity = expandedSize(1, 0); + UChar* d = allocChars(newCapacity); + if (!d) + m_rep = &Rep::null; + else { + d[0] = c; + m_rep = Rep::create(d, 1); + m_rep->capacity = newCapacity; + } + } else if (m_rep->baseIsSelf() && m_rep->rc == 1) { + // this is direct and has refcount of 1 (so we can just alter it directly) + expandCapacity(thisOffset + length + 1); + UChar *d = const_cast<UChar *>(data()); + if (d) { + d[length] = c; + m_rep->len = length + 1; + m_rep->_hash = 0; + } + } else if (thisOffset + length == usedCapacity() && length >= minShareSize) { + // this reaches the end of the string - extend it and share + expandCapacity(thisOffset + length + 1); + UChar *d = const_cast<UChar *>(data()); + if (d) { + d[length] = c; + m_rep = Rep::create(m_rep, 0, length + 1); + } + } else { + // this is shared with someone using more capacity, gotta make a whole new string + size_t newCapacity = expandedSize(length + 1, 0); + UChar* d = allocChars(newCapacity); + if (!d) + m_rep = &Rep::null; + else { + memcpy(d, data(), length * sizeof(UChar)); + d[length] = c; + m_rep = Rep::create(d, length + 1); + m_rep->capacity = newCapacity; + } + } + + return *this; +} + +CString UString::cstring() const +{ + return ascii(); +} + +char *UString::ascii() const +{ + // Never make the buffer smaller than normalStatBufferSize. + // Thus we almost never need to reallocate. + int length = size(); + int neededSize = length + 1; + if (neededSize < normalStatBufferSize) { + neededSize = normalStatBufferSize; + } + if (neededSize != statBufferSize) { + delete [] statBuffer; + statBuffer = new char [neededSize]; + statBufferSize = neededSize; + } + + const UChar *p = data(); + char *q = statBuffer; + const UChar *limit = p + length; + while (p != limit) { + *q = static_cast<char>(p->uc); + ++p; + ++q; + } + *q = '\0'; + + return statBuffer; +} + +UString &UString::operator=(const char *c) +{ + if (!c) { + m_rep = &Rep::null; + return *this; + } + + if (!c[0]) { + m_rep = &Rep::empty; + return *this; + } + + int l = static_cast<int>(strlen(c)); + UChar *d; + if (m_rep->rc == 1 && l <= m_rep->capacity && m_rep->baseIsSelf() && m_rep->offset == 0 && m_rep->preCapacity == 0) { + d = m_rep->buf; + m_rep->_hash = 0; + m_rep->len = l; + } else { + d = allocChars(l); + if (!d) { + m_rep = &Rep::null; + return *this; + } + m_rep = Rep::create(d, l); + } + for (int i = 0; i < l; i++) + d[i].uc = c[i]; + + return *this; +} + +bool UString::is8Bit() const +{ + const UChar *u = data(); + const UChar *limit = u + size(); + while (u < limit) { + if (u->uc > 0xFF) + return false; + ++u; + } + + return true; +} + +const UChar UString::operator[](int pos) const +{ + if (pos >= size()) + return '\0'; + return data()[pos]; +} + +double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const +{ + double d; + + // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk + // after the number, so is8Bit is too strict a check. + if (!is8Bit()) + return NaN; + + const char *c = ascii(); + + // skip leading white space + while (isASCIISpace(*c)) + c++; + + // empty string ? + if (*c == '\0') + return tolerateEmptyString ? 0.0 : NaN; + + // hex number ? + if (*c == '0' && (*(c+1) == 'x' || *(c+1) == 'X')) { + const char* firstDigitPosition = c + 2; + c++; + d = 0.0; + while (*(++c)) { + if (*c >= '0' && *c <= '9') + d = d * 16.0 + *c - '0'; + else if ((*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f')) + d = d * 16.0 + (*c & 0xdf) - 'A' + 10.0; + else + break; + } + + if (d >= mantissaOverflowLowerBound) + d = parseIntOverflow(firstDigitPosition, c - firstDigitPosition, 16); + } else { + // regular number ? + char *end; + d = kjs_strtod(c, &end); + if ((d != 0.0 || end != c) && d != Inf && d != -Inf) { + c = end; + } else { + double sign = 1.0; + + if (*c == '+') + c++; + else if (*c == '-') { + sign = -1.0; + c++; + } + + // We used strtod() to do the conversion. However, strtod() handles + // infinite values slightly differently than JavaScript in that it + // converts the string "inf" with any capitalization to infinity, + // whereas the ECMA spec requires that it be converted to NaN. + + if (c[0] == 'I' && c[1] == 'n' && c[2] == 'f' && c[3] == 'i' && c[4] == 'n' && c[5] == 'i' && c[6] == 't' && c[7] == 'y') { + d = sign * Inf; + c += 8; + } else if ((d == Inf || d == -Inf) && *c != 'I' && *c != 'i') + c = end; + else + return NaN; + } + } + + // allow trailing white space + while (isASCIISpace(*c)) + c++; + // don't allow anything after - unless tolerant=true + if (!tolerateTrailingJunk && *c != '\0') + d = NaN; + + return d; +} + +double UString::toDouble(bool tolerateTrailingJunk) const +{ + return toDouble(tolerateTrailingJunk, true); +} + +double UString::toDouble() const +{ + return toDouble(false, true); +} + +uint32_t UString::toUInt32(bool *ok) const +{ + double d = toDouble(); + bool b = true; + + if (d != static_cast<uint32_t>(d)) { + b = false; + d = 0; + } + + if (ok) + *ok = b; + + return static_cast<uint32_t>(d); +} + +uint32_t UString::toUInt32(bool *ok, bool tolerateEmptyString) const +{ + double d = toDouble(false, tolerateEmptyString); + bool b = true; + + if (d != static_cast<uint32_t>(d)) { + b = false; + d = 0; + } + + if (ok) + *ok = b; + + return static_cast<uint32_t>(d); +} + +uint32_t UString::toStrictUInt32(bool *ok) const +{ + if (ok) + *ok = false; + + // Empty string is not OK. + int len = m_rep->len; + if (len == 0) + return 0; + const UChar *p = m_rep->data(); + unsigned short c = p->unicode(); + + // If the first digit is 0, only 0 itself is OK. + if (c == '0') { + if (len == 1 && ok) + *ok = true; + return 0; + } + + // Convert to UInt32, checking for overflow. + uint32_t i = 0; + while (1) { + // Process character, turning it into a digit. + if (c < '0' || c > '9') + return 0; + const unsigned d = c - '0'; + + // Multiply by 10, checking for overflow out of 32 bits. + if (i > 0xFFFFFFFFU / 10) + return 0; + i *= 10; + + // Add in the digit, checking for overflow out of 32 bits. + const unsigned max = 0xFFFFFFFFU - d; + if (i > max) + return 0; + i += d; + + // Handle end of string. + if (--len == 0) { + if (ok) + *ok = true; + return i; + } + + // Get next character. + c = (++p)->unicode(); + } +} + +int UString::find(const UString &f, int pos) const +{ + int sz = size(); + int fsz = f.size(); + if (sz < fsz) + return -1; + if (pos < 0) + pos = 0; + if (fsz == 0) + return pos; + const UChar *end = data() + sz - fsz; + int fsizeminusone = (fsz - 1) * sizeof(UChar); + const UChar *fdata = f.data(); + unsigned short fchar = fdata->uc; + ++fdata; + for (const UChar *c = data() + pos; c <= end; c++) + if (c->uc == fchar && !memcmp(c + 1, fdata, fsizeminusone)) + return static_cast<int>(c - data()); + + return -1; +} + +int UString::find(UChar ch, int pos) const +{ + if (pos < 0) + pos = 0; + const UChar *end = data() + size(); + for (const UChar *c = data() + pos; c < end; c++) + if (*c == ch) + return static_cast<int>(c - data()); + + return -1; +} + +int UString::rfind(const UString &f, int pos) const +{ + int sz = size(); + int fsz = f.size(); + if (sz < fsz) + return -1; + if (pos < 0) + pos = 0; + if (pos > sz - fsz) + pos = sz - fsz; + if (fsz == 0) + return pos; + int fsizeminusone = (fsz - 1) * sizeof(UChar); + const UChar *fdata = f.data(); + for (const UChar *c = data() + pos; c >= data(); c--) { + if (*c == *fdata && !memcmp(c + 1, fdata + 1, fsizeminusone)) + return static_cast<int>(c - data()); + } + + return -1; +} + +int UString::rfind(UChar ch, int pos) const +{ + if (isEmpty()) + return -1; + if (pos + 1 >= size()) + pos = size() - 1; + for (const UChar *c = data() + pos; c >= data(); c--) { + if (*c == ch) + return static_cast<int>(c-data()); + } + + return -1; +} + +UString UString::substr(int pos, int len) const +{ + int s = size(); + + if (pos < 0) + pos = 0; + else if (pos >= s) + pos = s; + if (len < 0) + len = s; + if (pos + len >= s) + len = s - pos; + + if (pos == 0 && len == s) + return *this; + + return UString(Rep::create(m_rep, pos, len)); +} + +bool operator==(const UString& s1, const UString& s2) +{ + if (s1.m_rep->len != s2.m_rep->len) + return false; + + return (memcmp(s1.m_rep->data(), s2.m_rep->data(), + s1.m_rep->len * sizeof(UChar)) == 0); +} + +bool operator==(const UString& s1, const char *s2) +{ + if (s2 == 0) { + return s1.isEmpty(); + } + + const UChar *u = s1.data(); + const UChar *uend = u + s1.size(); + while (u != uend && *s2) { + if (u->uc != (unsigned char)*s2) + return false; + s2++; + u++; + } + + return u == uend && *s2 == 0; +} + +bool operator<(const UString& s1, const UString& s2) +{ + const int l1 = s1.size(); + const int l2 = s2.size(); + const int lmin = l1 < l2 ? l1 : l2; + const UChar *c1 = s1.data(); + const UChar *c2 = s2.data(); + int l = 0; + while (l < lmin && *c1 == *c2) { + c1++; + c2++; + l++; + } + if (l < lmin) + return (c1->uc < c2->uc); + + return (l1 < l2); +} + +int compare(const UString& s1, const UString& s2) +{ + const int l1 = s1.size(); + const int l2 = s2.size(); + const int lmin = l1 < l2 ? l1 : l2; + const UChar *c1 = s1.data(); + const UChar *c2 = s2.data(); + int l = 0; + while (l < lmin && *c1 == *c2) { + c1++; + c2++; + l++; + } + + if (l < lmin) + return (c1->uc > c2->uc) ? 1 : -1; + + if (l1 == l2) + return 0; + + return (l1 > l2) ? 1 : -1; +} + +CString UString::UTF8String(bool strict) const +{ + // Allocate a buffer big enough to hold all the characters. + const int length = size(); + Vector<char, 1024> buffer(length * 3); + + // Convert to runs of 8-bit characters. + char* p = buffer.data(); + const ::UChar* d = reinterpret_cast<const ::UChar*>(&data()->uc); + ConversionResult result = convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), strict); + if (result != conversionOK) + return CString(); + + return CString(buffer.data(), p - buffer.data()); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/ustring.h b/JavaScriptCore/kjs/ustring.h new file mode 100644 index 0000000..7faa86a --- /dev/null +++ b/JavaScriptCore/kjs/ustring.h @@ -0,0 +1,470 @@ +// -*- c-basic-offset: 2 -*- +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 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 _KJS_USTRING_H_ +#define _KJS_USTRING_H_ + +#include "JSLock.h" +#include "collector.h" +#include <stdint.h> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +/* On some ARM platforms GCC won't pack structures by default so sizeof(UChar) + will end up being != 2 which causes crashes since the code depends on that. */ +#if COMPILER(GCC) && PLATFORM(FORCE_PACK) +#define PACK_STRUCT __attribute__((packed)) +#else +#define PACK_STRUCT +#endif + +/** + * @internal + */ +namespace DOM { + class DOMString; + class AtomicString; +} +class KJScript; + +namespace KJS { + + using WTF::PlacementNewAdoptType; + using WTF::PlacementNewAdopt; + + class UString; + + /** + * @short Unicode character. + * + * UChar represents a 16 bit Unicode character. It's internal data + * representation is compatible to XChar2b and QChar. It's therefore + * possible to exchange data with X and Qt with shallow copies. + */ + struct UChar { + /** + * Construct a character with uninitialized value. + */ + UChar(); + /** + * Construct a character with the value denoted by the arguments. + * @param h higher byte + * @param l lower byte + */ + UChar(unsigned char h , unsigned char l); + /** + * Construct a character with the given value. + * @param u 16 bit Unicode value + */ + UChar(char u); + UChar(unsigned char u); + UChar(unsigned short u); + /** + * @return The higher byte of the character. + */ + unsigned char high() const { return static_cast<unsigned char>(uc >> 8); } + /** + * @return The lower byte of the character. + */ + unsigned char low() const { return static_cast<unsigned char>(uc); } + /** + * @return the 16 bit Unicode value of the character + */ + unsigned short unicode() const { return uc; } + + unsigned short uc; + } PACK_STRUCT; + + inline UChar::UChar() { } + inline UChar::UChar(unsigned char h , unsigned char l) : uc(h << 8 | l) { } + inline UChar::UChar(char u) : uc((unsigned char)u) { } + inline UChar::UChar(unsigned char u) : uc(u) { } + inline UChar::UChar(unsigned short u) : uc(u) { } + + /** + * @short 8 bit char based string class + */ + class CString { + public: + CString() : data(0), length(0) { } + CString(const char *c); + CString(const char *c, size_t len); + CString(const CString &); + + ~CString(); + + CString &append(const CString &); + CString &operator=(const char *c); + CString &operator=(const CString &); + CString &operator+=(const CString &c) { return append(c); } + + size_t size() const { return length; } + const char *c_str() const { return data; } + private: + char *data; + size_t length; + }; + + /** + * @short Unicode string class + */ + class UString { + friend bool operator==(const UString&, const UString&); + + public: + /** + * @internal + */ + struct Rep { + + static PassRefPtr<Rep> create(UChar *d, int l); + static PassRefPtr<Rep> createCopying(const UChar *d, int l); + static PassRefPtr<Rep> create(PassRefPtr<Rep> base, int offset, int length); + + void destroy(); + + bool baseIsSelf() const { return baseString == this; } + UChar* data() const { return baseString->buf + baseString->preCapacity + offset; } + int size() const { return len; } + + unsigned hash() const { if (_hash == 0) _hash = computeHash(data(), len); return _hash; } + unsigned computedHash() const { ASSERT(_hash); return _hash; } // fast path for Identifiers + + static unsigned computeHash(const UChar *, int length); + static unsigned computeHash(const char *); + + Rep* ref() { ASSERT(JSLock::lockCount() > 0); ++rc; return this; } + ALWAYS_INLINE void deref() { ASSERT(JSLock::lockCount() > 0); if (--rc == 0) destroy(); } + + // unshared data + int offset; + int len; + int rc; + mutable unsigned _hash; + bool isIdentifier; + UString::Rep* baseString; + size_t reportedCost; + + // potentially shared data + UChar *buf; + int usedCapacity; + int capacity; + int usedPreCapacity; + int preCapacity; + + static Rep null; + static Rep empty; + }; + + public: + + /** + * Constructs a null string. + */ + UString(); + /** + * Constructs a string from a classical zero-terminated char string. + */ + UString(const char *c); + /** + * Constructs a string from an array of Unicode characters of the specified + * length. + */ + UString(const UChar *c, int length); + /** + * If copy is false the string data will be adopted. + * That means that the data will NOT be copied and the pointer will + * be deleted when the UString object is modified or destroyed. + * Behaviour defaults to a deep copy if copy is true. + */ + UString(UChar *c, int length, bool copy); + /** + * Copy constructor. Makes a shallow copy only. + */ + UString(const UString &s) : m_rep(s.m_rep) {} + + UString(const Vector<UChar>& buffer); + + /** + * Convenience declaration only ! You'll be on your own to write the + * implementation for a construction from DOM::DOMString. + * + * Note: feel free to contact me if you want to see a dummy header for + * your favorite FooString class here ! + */ + UString(const DOM::DOMString&); + /** + * Convenience declaration only ! See UString(const DOM::DOMString&). + */ + UString(const DOM::AtomicString&); + + /** + * Concatenation constructor. Makes operator+ more efficient. + */ + UString(const UString &, const UString &); + /** + * Destructor. + */ + ~UString() {} + + // Special constructor for cases where we overwrite an object in place. + UString(PlacementNewAdoptType) : m_rep(PlacementNewAdopt) { } + + /** + * Constructs a string from an int. + */ + static UString from(int i); + /** + * Constructs a string from an unsigned int. + */ + static UString from(unsigned int u); + /** + * Constructs a string from a long int. + */ + static UString from(long u); + /** + * Constructs a string from a double. + */ + static UString from(double d); + + struct Range { + public: + Range(int pos, int len) : position(pos), length(len) {} + Range() {} + int position; + int length; + }; + + UString spliceSubstringsWithSeparators(const Range *substringRanges, int rangeCount, const UString *separators, int separatorCount) const; + + /** + * Append another string. + */ + UString &append(const UString &); + UString &append(const char *); + UString &append(unsigned short); + UString &append(char c) { return append(static_cast<unsigned short>(static_cast<unsigned char>(c))); } + UString &append(UChar c) { return append(c.uc); } + + /** + * @return The string converted to the 8-bit string type CString(). + * This method is not Unicode safe and shouldn't be used unless the string + * is known to be ASCII. + */ + CString cstring() const; + /** + * Convert the Unicode string to plain ASCII chars chopping of any higher + * bytes. This method should only be used for *debugging* purposes as it + * is neither Unicode safe nor free from side effects. In order not to + * waste any memory the char buffer is static and *shared* by all UString + * instances. + */ + char *ascii() const; + + /** + * Convert the string to UTF-8, assuming it is UTF-16 encoded. + * In non-strict mode, this function is tolerant of badly formed UTF-16, it + * can create UTF-8 strings that are invalid because they have characters in + * the range U+D800-U+DDFF, U+FFFE, or U+FFFF, but the UTF-8 string is + * guaranteed to be otherwise valid. + * In strict mode, error is returned as null CString. + */ + CString UTF8String(bool strict = false) const; + + /** + * @see UString(const DOM::DOMString&). + */ + DOM::DOMString domString() const; + + /** + * Assignment operator. + */ + UString &operator=(const char *c); + /** + * Appends the specified string. + */ + UString &operator+=(const UString &s) { return append(s); } + UString &operator+=(const char *s) { return append(s); } + + /** + * @return A pointer to the internal Unicode data. + */ + const UChar* data() const { return m_rep->data(); } + /** + * @return True if null. + */ + bool isNull() const { return (m_rep == &Rep::null); } + /** + * @return True if null or zero length. + */ + bool isEmpty() const { return (!m_rep->len); } + /** + * Use this if you want to make sure that this string is a plain ASCII + * string. For example, if you don't want to lose any information when + * using cstring() or ascii(). + * + * @return True if the string doesn't contain any non-ASCII characters. + */ + bool is8Bit() const; + /** + * @return The length of the string. + */ + int size() const { return m_rep->size(); } + /** + * Const character at specified position. + */ + const UChar operator[](int pos) const; + + /** + * Attempts an conversion to a number. Apart from floating point numbers, + * the algorithm will recognize hexadecimal representations (as + * indicated by a 0x or 0X prefix) and +/- Infinity. + * Returns NaN if the conversion failed. + * @param tolerateTrailingJunk if true, toDouble can tolerate garbage after the number. + * @param tolerateEmptyString if false, toDouble will turn an empty string into NaN rather than 0. + */ + double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const; + double toDouble(bool tolerateTrailingJunk) const; + double toDouble() const; + + /** + * Attempts an conversion to a 32-bit integer. ok will be set + * according to the success. + * @param tolerateEmptyString if false, toUInt32 will return false for *ok for an empty string. + */ + uint32_t toUInt32(bool *ok = 0) const; + uint32_t toUInt32(bool *ok, bool tolerateEmptyString) const; + uint32_t toStrictUInt32(bool *ok = 0) const; + + /** + * Attempts an conversion to an array index. The "ok" boolean will be set + * to true if it is a valid array index according to the rule from + * ECMA 15.2 about what an array index is. It must exactly match the string + * form of an unsigned integer, and be less than 2^32 - 1. + */ + unsigned toArrayIndex(bool *ok = 0) const; + + /** + * @return Position of first occurrence of f starting at position pos. + * -1 if the search was not successful. + */ + int find(const UString &f, int pos = 0) const; + int find(UChar, int pos = 0) const; + /** + * @return Position of first occurrence of f searching backwards from + * position pos. + * -1 if the search was not successful. + */ + int rfind(const UString &f, int pos) const; + int rfind(UChar, int pos) const; + /** + * @return The sub string starting at position pos and length len. + */ + UString substr(int pos = 0, int len = -1) const; + /** + * Static instance of a null string. + */ + static const UString &null(); + + Rep* rep() const { return m_rep.get(); } + UString(PassRefPtr<Rep> r) : m_rep(r) { ASSERT(m_rep); } + + size_t cost() const; + + private: + size_t expandedSize(size_t size, size_t otherSize) const; + int usedCapacity() const; + int usedPreCapacity() const; + void expandCapacity(int requiredLength); + void expandPreCapacity(int requiredPreCap); + + RefPtr<Rep> m_rep; + }; + + inline bool operator==(const UChar &c1, const UChar &c2) { + return (c1.uc == c2.uc); + } + bool operator==(const UString& s1, const UString& s2); + inline bool operator!=(const UString& s1, const UString& s2) { + return !KJS::operator==(s1, 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 !KJS::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 !KJS::operator==(s1, s2); + } + bool operator==(const CString& s1, const CString& s2); + inline UString operator+(const UString& s1, const UString& s2) { + return UString(s1, s2); + } + + int compare(const UString &, const UString &); + +inline UString::UString() + : m_rep(&Rep::null) +{ +} + +// 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 UString::toArrayIndex(bool *ok) const +{ + unsigned i = toStrictUInt32(ok); + if (ok && i >= 0xFFFFFFFFU) + *ok = false; + return i; +} + +// We'd rather not do shared substring append for small strings, since +// this runs too much risk of a tiny initial string holding down a +// huge buffer. +// FIXME: this should be size_t but that would cause warnings until we +// fix UString sizes to be size_t instead of int +static const int minShareSize = Collector::minExtraCostSize / sizeof(UChar); + +inline size_t UString::cost() const +{ + size_t capacity = (m_rep->baseString->capacity + m_rep->baseString->preCapacity) * sizeof(UChar); + size_t reportedCost = m_rep->baseString->reportedCost; + ASSERT(capacity >= reportedCost); + + size_t capacityDelta = capacity - reportedCost; + + if (capacityDelta < static_cast<size_t>(minShareSize)) + return 0; + + m_rep->baseString->reportedCost = capacity; + return capacityDelta; +} + +} // namespace + +#endif diff --git a/JavaScriptCore/kjs/value.cpp b/JavaScriptCore/kjs/value.cpp new file mode 100644 index 0000000..55da40b --- /dev/null +++ b/JavaScriptCore/kjs/value.cpp @@ -0,0 +1,232 @@ +/* + * 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 "value.h" + +#include "error_object.h" +#include "nodes.h" +#include <stdio.h> +#include <string.h> +#include <wtf/MathExtras.h> + +namespace KJS { + +#if defined NAN && defined INFINITY + +extern const double NaN = NAN; +extern const double Inf = INFINITY; + +#else // !(defined NAN && defined INFINITY) + +// The trick is to define the NaN and Inf globals with a different type than the declaration. +// This trick works because the mangled name of the globals does not include the type, although +// I'm not sure that's guaranteed. There could be alignment issues with this, since arrays of +// characters don't necessarily need the same alignment doubles do, but for now it seems to work. +// It would be good to figure out a 100% clean way that still avoids code that runs at init time. + +// Note, we have to use union to ensure alignment. Otherwise, NaN_Bytes can start anywhere, +// while NaN_double has to be 4-byte aligned for 32-bits. +// With -fstrict-aliasing enabled, unions are the only safe way to do type masquerading. + +static const union { + struct { + unsigned char NaN_Bytes[8]; + unsigned char Inf_Bytes[8]; + } bytes; + + struct { + double NaN_Double; + double Inf_Double; + } doubles; + +} NaNInf = { { +#if PLATFORM(BIG_ENDIAN) + { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 }, + { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } +#elif PLATFORM(MIDDLE_ENDIAN) + { 0, 0, 0xf8, 0x7f, 0, 0, 0, 0 }, + { 0, 0, 0xf0, 0x7f, 0, 0, 0, 0 } +#else + { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f }, + { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } +#endif +} } ; + +extern const double NaN = NaNInf.doubles.NaN_Double; +extern const double Inf = NaNInf.doubles.Inf_Double; + +#endif // !(defined NAN && defined INFINITY) + +static const double D16 = 65536.0; +static const double D32 = 4294967296.0; + +void *JSCell::operator new(size_t size) +{ + return Collector::allocate(size); +} + +bool JSCell::getUInt32(uint32_t&) const +{ + return false; +} + +bool JSCell::getTruncatedInt32(int32_t&) const +{ + return false; +} + +bool JSCell::getTruncatedUInt32(uint32_t&) const +{ + return false; +} + +// ECMA 9.4 +double JSValue::toInteger(ExecState *exec) const +{ + int32_t i; + if (getTruncatedInt32(i)) + return i; + double d = toNumber(exec); + return isnan(d) ? 0.0 : trunc(d); +} + +double JSValue::toIntegerPreserveNaN(ExecState *exec) const +{ + int32_t i; + if (getTruncatedInt32(i)) + return i; + return trunc(toNumber(exec)); +} + +int32_t JSValue::toInt32SlowCase(double d, bool& ok) +{ + ok = true; + + if (d >= -D32 / 2 && d < D32 / 2) + return static_cast<int32_t>(d); + + if (isnan(d) || isinf(d)) { + ok = false; + return 0; + } + + double d32 = fmod(trunc(d), D32); + if (d32 >= D32 / 2) + d32 -= D32; + else if (d32 < -D32 / 2) + d32 += D32; + return static_cast<int32_t>(d32); +} + +int32_t JSValue::toInt32SlowCase(ExecState* exec, bool& ok) const +{ + return JSValue::toInt32SlowCase(toNumber(exec), ok); +} + +uint32_t JSValue::toUInt32SlowCase(double d, bool& ok) +{ + ok = true; + + if (d >= 0.0 && d < D32) + return static_cast<uint32_t>(d); + + if (isnan(d) || isinf(d)) { + ok = false; + return 0; + } + + double d32 = fmod(trunc(d), D32); + if (d32 < 0) + d32 += D32; + return static_cast<uint32_t>(d32); +} + +uint32_t JSValue::toUInt32SlowCase(ExecState* exec, bool& ok) const +{ + return JSValue::toUInt32SlowCase(toNumber(exec), ok); +} + +float JSValue::toFloat(ExecState* exec) const +{ + return static_cast<float>(toNumber(exec)); +} + +bool JSCell::getNumber(double &numericValue) const +{ + if (!isNumber()) + return false; + numericValue = static_cast<const NumberImp *>(this)->value(); + return true; +} + +double JSCell::getNumber() const +{ + return isNumber() ? static_cast<const NumberImp *>(this)->value() : NaN; +} + +bool JSCell::getString(UString &stringValue) const +{ + if (!isString()) + return false; + stringValue = static_cast<const StringImp *>(this)->value(); + return true; +} + +UString JSCell::getString() const +{ + return isString() ? static_cast<const StringImp *>(this)->value() : UString(); +} + +JSObject *JSCell::getObject() +{ + return isObject() ? static_cast<JSObject *>(this) : 0; +} + +const JSObject *JSCell::getObject() const +{ + return isObject() ? static_cast<const JSObject *>(this) : 0; +} + +JSCell* jsString(const char* s) +{ + return new StringImp(s ? s : ""); +} + +JSCell* jsString(const UString& s) +{ + return s.isNull() ? new StringImp("") : new StringImp(s); +} + +JSCell* jsOwnedString(const UString& s) +{ + return s.isNull() ? new StringImp("", StringImp::HasOtherOwner) : new StringImp(s, StringImp::HasOtherOwner); +} + +// This method includes a PIC branch to set up the NumberImp's vtable, so we quarantine +// it in a separate function to keep the normal case speedy. +JSValue *jsNumberCell(double d) +{ + return new NumberImp(d); +} + +} // namespace KJS diff --git a/JavaScriptCore/kjs/value.h b/JavaScriptCore/kjs/value.h new file mode 100644 index 0000000..5ebb575 --- /dev/null +++ b/JavaScriptCore/kjs/value.h @@ -0,0 +1,523 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 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 KJS_VALUE_H +#define KJS_VALUE_H + +#include "JSImmediate.h" +#include "collector.h" +#include "ustring.h" +#include <stddef.h> // for size_t + +namespace KJS { + +class ExecState; +class JSObject; +class JSCell; + +struct ClassInfo; + +/** + * JSValue is the base type for all primitives (Undefined, Null, Boolean, + * String, Number) and objects in ECMAScript. + * + * Note: you should never inherit from JSValue as it is for primitive types + * only (all of which are provided internally by KJS). Instead, inherit from + * JSObject. + */ +class JSValue : Noncopyable { + friend class JSCell; // so it can derive from this class + friend class Collector; // so it can call asCell() + +private: + JSValue(); + virtual ~JSValue(); + +public: + // Querying the type. + JSType type() const; + bool isUndefined() const; + bool isNull() const; + bool isUndefinedOrNull() const; + bool isBoolean() const; + bool isNumber() const; + bool isString() const; + bool isObject() const; + bool isObject(const ClassInfo *) const; + + // Extracting the value. + bool getBoolean(bool&) const; + bool getBoolean() const; // false if not a boolean + bool getNumber(double&) const; + double getNumber() const; // NaN if not a number + bool getString(UString&) const; + UString getString() const; // null string if not a string + JSObject *getObject(); // NULL if not an object + const JSObject *getObject() const; // NULL if not an object + + // Extracting integer values. + bool getUInt32(uint32_t&) const; + bool getTruncatedInt32(int32_t&) const; + bool getTruncatedUInt32(uint32_t&) const; + + // Basic conversions. + JSValue* toPrimitive(ExecState* exec, JSType preferredType = UnspecifiedType) const; + bool getPrimitiveNumber(ExecState* exec, double& number, JSValue*& value); + + bool toBoolean(ExecState *exec) const; + double toNumber(ExecState *exec) const; + JSValue* toJSNumber(ExecState*) const; // Fast path for when you expect that the value is an immediate number. + UString toString(ExecState *exec) const; + JSObject *toObject(ExecState *exec) const; + + // Integer conversions. + double toInteger(ExecState*) const; + double toIntegerPreserveNaN(ExecState*) const; + int32_t toInt32(ExecState*) const; + int32_t toInt32(ExecState*, bool& ok) const; + uint32_t toUInt32(ExecState*) const; + uint32_t toUInt32(ExecState*, bool& ok) const; + + // These are identical logic to above, and faster than jsNumber(number)->toInt32(exec) + static int32_t toInt32(double); + static int32_t toUInt32(double); + + // Floating point conversions. + float toFloat(ExecState*) const; + + // Garbage collection. + void mark(); + bool marked() const; + + static int32_t toInt32SlowCase(double, bool& ok); + static uint32_t toUInt32SlowCase(double, bool& ok); + +private: + int32_t toInt32SlowCase(ExecState*, bool& ok) const; + uint32_t toUInt32SlowCase(ExecState*, bool& ok) const; + + // Implementation details. + JSCell *asCell(); + const JSCell *asCell() const; + + // Give a compile time error if we try to copy one of these. + JSValue(const JSValue&); + JSValue& operator=(const JSValue&); +}; + +class JSCell : public JSValue { + friend class Collector; + friend class NumberImp; + friend class StringImp; + friend class JSObject; + friend class GetterSetterImp; +private: + JSCell(); + virtual ~JSCell(); +public: + // Querying the type. + virtual JSType type() const = 0; + bool isNumber() const; + bool isString() const; + bool isObject() const; + bool isObject(const ClassInfo *) const; + + // Extracting the value. + bool getNumber(double&) const; + double getNumber() const; // NaN if not a number + bool getString(UString&) const; + UString getString() const; // null string if not a string + JSObject *getObject(); // NULL if not an object + const JSObject *getObject() const; // NULL if not an object + + // Extracting integer values. + virtual bool getUInt32(uint32_t&) const; + virtual bool getTruncatedInt32(int32_t&) const; + virtual bool getTruncatedUInt32(uint32_t&) const; + + // Basic conversions. + virtual JSValue *toPrimitive(ExecState *exec, JSType preferredType = UnspecifiedType) const = 0; + virtual bool getPrimitiveNumber(ExecState* exec, double& number, JSValue*& value) = 0; + virtual bool toBoolean(ExecState *exec) const = 0; + virtual double toNumber(ExecState *exec) const = 0; + virtual UString toString(ExecState *exec) const = 0; + virtual JSObject *toObject(ExecState *exec) const = 0; + + // Garbage collection. + void *operator new(size_t); + virtual void mark(); + bool marked() const; +}; + +JSValue *jsNumberCell(double); + +JSCell *jsString(const UString&); // returns empty string if passed null string +JSCell *jsString(const char* = ""); // returns empty string if passed 0 + +// 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 +JSCell *jsOwnedString(const UString&); + +extern const double NaN; +extern const double Inf; + +inline JSValue *jsUndefined() +{ + return JSImmediate::undefinedImmediate(); +} + +inline JSValue *jsNull() +{ + return JSImmediate::nullImmediate(); +} + +inline JSValue *jsNaN() +{ + static const union { + uint64_t bits; + double d; + } nan = { 0x7ff80000ULL << 32 }; + return jsNumberCell(nan.d); +} + +inline JSValue *jsBoolean(bool b) +{ + return b ? JSImmediate::trueImmediate() : JSImmediate::falseImmediate(); +} + +ALWAYS_INLINE JSValue* jsNumber(double d) +{ + JSValue* v = JSImmediate::from(d); + return v ? v : jsNumberCell(d); +} + +ALWAYS_INLINE JSValue* jsNumber(int i) +{ + JSValue* v = JSImmediate::from(i); + return v ? v : jsNumberCell(i); +} + +ALWAYS_INLINE JSValue* jsNumber(unsigned i) +{ + JSValue* v = JSImmediate::from(i); + return v ? v : jsNumberCell(i); +} + +ALWAYS_INLINE JSValue* jsNumber(long i) +{ + JSValue* v = JSImmediate::from(i); + return v ? v : jsNumberCell(i); +} + +ALWAYS_INLINE JSValue* jsNumber(unsigned long i) +{ + JSValue* v = JSImmediate::from(i); + return v ? v : jsNumberCell(i); +} + +ALWAYS_INLINE JSValue* jsNumber(long long i) +{ + JSValue* v = JSImmediate::from(i); + return v ? v : jsNumberCell(static_cast<double>(i)); +} + +ALWAYS_INLINE JSValue* jsNumber(unsigned long long i) +{ + JSValue* v = JSImmediate::from(i); + return v ? v : jsNumberCell(static_cast<double>(i)); +} + +ALWAYS_INLINE JSValue* jsNumberFromAnd(ExecState *exec, JSValue* v1, JSValue* v2) +{ + if (JSImmediate::areBothImmediateNumbers(v1, v2)) + return JSImmediate::andImmediateNumbers(v1, v2); + return jsNumber(v1->toInt32(exec) & v2->toInt32(exec)); +} + +inline JSValue::JSValue() +{ +} + +inline JSValue::~JSValue() +{ +} + +inline JSCell::JSCell() +{ +} + +inline JSCell::~JSCell() +{ +} + +inline bool JSCell::isNumber() const +{ + return type() == NumberType; +} + +inline bool JSCell::isString() const +{ + return type() == StringType; +} + +inline bool JSCell::isObject() const +{ + return type() == ObjectType; +} + +inline bool JSCell::marked() const +{ + return Collector::isCellMarked(this); +} + +inline void JSCell::mark() +{ + return Collector::markCell(this); +} + +ALWAYS_INLINE JSCell* JSValue::asCell() +{ + ASSERT(!JSImmediate::isImmediate(this)); + return static_cast<JSCell*>(this); +} + +ALWAYS_INLINE const JSCell* JSValue::asCell() const +{ + ASSERT(!JSImmediate::isImmediate(this)); + return static_cast<const JSCell*>(this); +} + +inline bool JSValue::isUndefined() const +{ + return this == jsUndefined(); +} + +inline bool JSValue::isNull() const +{ + return this == jsNull(); +} + +inline bool JSValue::isUndefinedOrNull() const +{ + return JSImmediate::isUndefinedOrNull(this); +} + +inline bool JSValue::isBoolean() const +{ + return JSImmediate::isBoolean(this); +} + +inline bool JSValue::isNumber() const +{ + return JSImmediate::isNumber(this) || (!JSImmediate::isImmediate(this) && asCell()->isNumber()); +} + +inline bool JSValue::isString() const +{ + return !JSImmediate::isImmediate(this) && asCell()->isString(); +} + +inline bool JSValue::isObject() const +{ + return !JSImmediate::isImmediate(this) && asCell()->isObject(); +} + +inline bool JSValue::getBoolean(bool& v) const +{ + if (JSImmediate::isBoolean(this)) { + v = JSImmediate::toBoolean(this); + return true; + } + + return false; +} + +inline bool JSValue::getBoolean() const +{ + return JSImmediate::isBoolean(this) ? JSImmediate::toBoolean(this) : false; +} + +inline bool JSValue::getNumber(double& v) const +{ + if (JSImmediate::isImmediate(this)) { + v = JSImmediate::toDouble(this); + return true; + } + return asCell()->getNumber(v); +} + +inline double JSValue::getNumber() const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::toDouble(this) : asCell()->getNumber(); +} + +inline bool JSValue::getString(UString& s) const +{ + return !JSImmediate::isImmediate(this) && asCell()->getString(s); +} + +inline UString JSValue::getString() const +{ + return JSImmediate::isImmediate(this) ? UString() : asCell()->getString(); +} + +inline JSObject *JSValue::getObject() +{ + return JSImmediate::isImmediate(this) ? 0 : asCell()->getObject(); +} + +inline const JSObject *JSValue::getObject() const +{ + return JSImmediate::isImmediate(this) ? 0 : asCell()->getObject(); +} + +ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::getUInt32(this, v) : asCell()->getUInt32(v); +} + +ALWAYS_INLINE bool JSValue::getTruncatedInt32(int32_t& v) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::getTruncatedInt32(this, v) : asCell()->getTruncatedInt32(v); +} + +inline bool JSValue::getTruncatedUInt32(uint32_t& v) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::getTruncatedUInt32(this, v) : asCell()->getTruncatedUInt32(v); +} + +inline void JSValue::mark() +{ + ASSERT(!JSImmediate::isImmediate(this)); // callers should check !marked() before calling mark() + asCell()->mark(); +} + +inline bool JSValue::marked() const +{ + return JSImmediate::isImmediate(this) || asCell()->marked(); +} + +inline JSType JSValue::type() const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::type(this) : asCell()->type(); +} + +inline JSValue* JSValue::toPrimitive(ExecState* exec, JSType preferredType) const +{ + return JSImmediate::isImmediate(this) ? const_cast<JSValue*>(this) : asCell()->toPrimitive(exec, preferredType); +} + +inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& value) +{ + if (JSImmediate::isImmediate(this)) { + number = JSImmediate::toDouble(this); + value = this; + return true; + } + return asCell()->getPrimitiveNumber(exec, number, value); +} + +inline bool JSValue::toBoolean(ExecState *exec) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::toBoolean(this) : asCell()->toBoolean(exec); +} + +ALWAYS_INLINE double JSValue::toNumber(ExecState *exec) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::toDouble(this) : asCell()->toNumber(exec); +} + +ALWAYS_INLINE JSValue* JSValue::toJSNumber(ExecState* exec) const +{ + return JSImmediate::isNumber(this) ? const_cast<JSValue*>(this) : jsNumber(this->toNumber(exec)); +} + +inline UString JSValue::toString(ExecState *exec) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::toString(this) : asCell()->toString(exec); +} + +inline JSObject* JSValue::toObject(ExecState* exec) const +{ + return JSImmediate::isImmediate(this) ? JSImmediate::toObject(this, exec) : asCell()->toObject(exec); +} + +ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const +{ + int32_t i; + if (getTruncatedInt32(i)) + return i; + bool ok; + return toInt32SlowCase(exec, ok); +} + +inline uint32_t JSValue::toUInt32(ExecState* exec) const +{ + uint32_t i; + if (getTruncatedUInt32(i)) + return i; + bool ok; + return toUInt32SlowCase(exec, ok); +} + +inline int32_t JSValue::toInt32(double val) +{ + if (!(val >= -2147483648.0 && val < 2147483648.0)) { + bool ignored; + return toInt32SlowCase(val, ignored); + } + return static_cast<int32_t>(val); +} + +inline int32_t JSValue::toUInt32(double val) +{ + if (!(val >= 0.0 && val < 4294967296.0)) { + bool ignored; + return toUInt32SlowCase(val, ignored); + } + return static_cast<uint32_t>(val); +} + +inline int32_t JSValue::toInt32(ExecState* exec, bool& ok) const +{ + int32_t i; + if (getTruncatedInt32(i)) { + ok = true; + return i; + } + return toInt32SlowCase(exec, ok); +} + +inline uint32_t JSValue::toUInt32(ExecState* exec, bool& ok) const +{ + uint32_t i; + if (getTruncatedUInt32(i)) { + ok = true; + return i; + } + return toUInt32SlowCase(exec, ok); +} + +} // namespace + +#endif // KJS_VALUE_H |