diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /JavaScriptCore/kjs | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'JavaScriptCore/kjs')
106 files changed, 10341 insertions, 29595 deletions
diff --git a/JavaScriptCore/kjs/Activation.h b/JavaScriptCore/kjs/Activation.h deleted file mode 100644 index 0fdd2cc..0000000 --- a/JavaScriptCore/kjs/Activation.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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*, int attr = None); - 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() { 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 deleted file mode 100644 index 8c19f80..0000000 --- a/JavaScriptCore/kjs/AllInOneFile.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// -*- 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. - * - */ - -// 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 deleted file mode 100644 index 6941b58..0000000 --- a/JavaScriptCore/kjs/CollectorHeapIntrospector.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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" - -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 deleted file mode 100644 index 76ba324..0000000 --- a/JavaScriptCore/kjs/CollectorHeapIntrospector.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5f673b8..0000000 --- a/JavaScriptCore/kjs/CommonIdentifiers.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 deleted file mode 100644 index ae5c778..0000000 --- a/JavaScriptCore/kjs/CommonIdentifiers.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 deleted file mode 100644 index 22816b3..0000000 --- a/JavaScriptCore/kjs/DateMath.cpp +++ /dev/null @@ -1,506 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6fdad0b..0000000 --- a/JavaScriptCore/kjs/DateMath.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 deleted file mode 100644 index b37523e..0000000 --- a/JavaScriptCore/kjs/ExecState.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// -*- 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 deleted file mode 100644 index b63c4ca..0000000 --- a/JavaScriptCore/kjs/ExecState.h +++ /dev/null @@ -1,234 +0,0 @@ -// -*- 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* e) { m_exception = e; } - void clearException() { m_exception = 0; } - JSValue* exception() const { return m_exception; } - JSValue** exceptionSlot() { return &m_exception; } - bool hadException() const { return !!m_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 deleted file mode 100644 index 4d7ea43..0000000 --- a/JavaScriptCore/kjs/JSGlobalObject.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/* - * 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 - -#ifdef ANDROID_INSTRUMENT -#define LOG_TAG "javascriptcore" -#undef LOG -#include <utils/Log.h> - -static unsigned sTotalTimeUsed; -#endif - -namespace KJS { - -// Default number of ticks before a timeout check should be done. -#ifdef ANDROID_MOBILE -static const int initialTickCountThreshold = 100; -static const int maxTickCountThreshold = 255; -#else -static const int initialTickCountThreshold = 255; -#endif - -// 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() -{ -#ifdef ANDROID_INSTRUMENT -#if defined(HAVE_POSIX_CLOCKS) - struct timespec tm; - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); - return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; -#endif -#endif -#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; - - d()->compatMode = NativeMode; - - 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, int attr) -{ - if (symbolTablePut(propertyName, value, !(attr & ~DontDelete))) - return; - return JSVariableObject::put(exec, propertyName, value, attr); -} - -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 | DontDelete | ReadOnly); - d()->functionPrototype->putDirect(exec->propertyNames().constructor, d()->functionConstructor, DontEnum | DontDelete | ReadOnly); - d()->arrayPrototype->putDirect(exec->propertyNames().constructor, d()->arrayConstructor, DontEnum | DontDelete | ReadOnly); - d()->booleanPrototype->putDirect(exec->propertyNames().constructor, d()->booleanConstructor, DontEnum | DontDelete | ReadOnly); - d()->stringPrototype->putDirect(exec->propertyNames().constructor, d()->stringConstructor, DontEnum | DontDelete | ReadOnly); - d()->numberPrototype->putDirect(exec->propertyNames().constructor, d()->numberConstructor, DontEnum | DontDelete | ReadOnly); - d()->datePrototype->putDirect(exec->propertyNames().constructor, d()->dateConstructor, DontEnum | DontDelete | ReadOnly); - d()->regExpPrototype->putDirect(exec->propertyNames().constructor, d()->regExpConstructor, DontEnum | DontDelete | ReadOnly); - d()->errorPrototype->putDirect(exec->propertyNames().constructor, d()->errorConstructor, DontEnum | DontDelete | ReadOnly); - d()->evalErrorPrototype->putDirect(exec->propertyNames().constructor, d()->evalErrorConstructor, DontEnum | DontDelete | ReadOnly); - d()->rangeErrorPrototype->putDirect(exec->propertyNames().constructor, d()->rangeErrorConstructor, DontEnum | DontDelete | ReadOnly); - d()->referenceErrorPrototype->putDirect(exec->propertyNames().constructor, d()->referenceErrorConstructor, DontEnum | DontDelete | ReadOnly); - d()->syntaxErrorPrototype->putDirect(exec->propertyNames().constructor, d()->syntaxErrorConstructor, DontEnum | DontDelete | ReadOnly); - d()->typeErrorPrototype->putDirect(exec->propertyNames().constructor, d()->typeErrorConstructor, DontEnum | DontDelete | ReadOnly); - d()->URIErrorPrototype->putDirect(exec->propertyNames().constructor, d()->URIErrorConstructor, DontEnum | DontDelete | ReadOnly); - - // Set global constructors - - // FIXME: kjs_window.cpp checks Internal/DontEnum as a performance hack, to - // see that these values can be put directly without a check for override - // properties. - - // FIXME: These properties should 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, Internal); - putDirect("RangeError", d()->rangeErrorConstructor, Internal); - putDirect("ReferenceError", d()->referenceErrorConstructor, Internal); - putDirect("SyntaxError", d()->syntaxErrorConstructor, Internal); - putDirect("TypeError", d()->typeErrorConstructor, Internal); - putDirect("URIError", d()->URIErrorConstructor, Internal); - - // 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(); -#ifdef ANDROID_INSTRUMENT - if (d()->timeoutCheckCount == 0) - d()->startTime = getCurrentTime(); -#endif - ++d()->timeoutCheckCount; -} - -void JSGlobalObject::stopTimeoutCheck() -{ - --d()->timeoutCheckCount; -#ifdef ANDROID_INSTRUMENT - if (d()->timeoutCheckCount == 0) { - unsigned time = getCurrentTime() - d()->startTime; - sTotalTimeUsed += time; - if (time > 1000) - LOGW("***** JavaScript used %d ms\n", time); - } -#endif -} - -#ifdef ANDROID_INSTRUMENT -void JSGlobalObject::resetTimeCounter() -{ - sTotalTimeUsed = 0; -} - -void JSGlobalObject::reportTimeCounter() -{ - LOG(LOG_DEBUG, "WebCore", "*-* Total JavaScript (may include parsing, layout, or calcStyle) time: %d ms\n", - sTotalTimeUsed); -} -#endif - -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; - -#ifdef ANDROID_MOBILE - if (d()->ticksUntilNextTimeoutCheck > (unsigned)maxTickCountThreshold) - d()->ticksUntilNextTimeoutCheck = maxTickCountThreshold; -#endif - - if (d()->timeoutTime && d()->timeExecuting > d()->timeoutTime) { - if (shouldInterruptScript()) -#ifdef ANDROID_INSTRUMENT - { - LOGW("***** JavaScript got interrupted\n"); - return true; - } -#else - return true; -#endif - - 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 deleted file mode 100644 index 428a17c..0000000 --- a/JavaScriptCore/kjs/JSGlobalObject.h +++ /dev/null @@ -1,269 +0,0 @@ -// -*- 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; - - enum CompatMode { NativeMode, IECompat, NetscapeCompat }; - - 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; - CompatMode compatMode; - - GlobalExecState globalExec; - int recursion; - - unsigned timeoutTime; - unsigned timeAtLastCheckTimeout; - unsigned timeExecuting; - unsigned timeoutCheckCount; - unsigned tickCount; - unsigned ticksUntilNextTimeoutCheck; - -#ifdef ANDROID_MOBILE - unsigned startTime; -#endif - 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*, int attr = None); - - // 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(); - -#ifdef ANDROID_INSTRUMENT - static void resetTimeCounter(); - static void reportTimeCounter(); -#endif - - Debugger* debugger() const { return d()->debugger; } - void setDebugger(Debugger* debugger) { d()->debugger = debugger; } - - // FIXME: Let's just pick one compatible behavior and go with it. - void setCompatMode(CompatMode mode) { d()->compatMode = mode; } - CompatMode compatMode() const { return d()->compatMode; } - - 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 deleted file mode 100644 index ded8ccb..0000000 --- a/JavaScriptCore/kjs/JSImmediate.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 deleted file mode 100644 index bc0cb16..0000000 --- a/JavaScriptCore/kjs/JSImmediate.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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 deleted file mode 100644 index 9d51ca9..0000000 --- a/JavaScriptCore/kjs/JSLock.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// -*- 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 deleted file mode 100644 index f6cda5d..0000000 --- a/JavaScriptCore/kjs/JSLock.h +++ /dev/null @@ -1,81 +0,0 @@ -// -*- 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 deleted file mode 100644 index 1c9418c..0000000 --- a/JavaScriptCore/kjs/JSType.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 deleted file mode 100644 index 76d563d..0000000 --- a/JavaScriptCore/kjs/JSVariableObject.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 deleted file mode 100644 index e82013b..0000000 --- a/JavaScriptCore/kjs/JSVariableObject.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 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 checkReadOnly); - - 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, bool checkReadOnly) - { - size_t index = symbolTable().get(propertyName.ustring().rep()); - if (index == missingSymbolMarker()) - return false; - LocalStorageEntry& entry = d->localStorage[index]; - if (checkReadOnly && (entry.attributes & ReadOnly)) - return true; - entry.value = value; - return true; - } - -} // namespace KJS - -#endif // JSVariableObject_h diff --git a/JavaScriptCore/kjs/JSWrapperObject.cpp b/JavaScriptCore/kjs/JSWrapperObject.cpp deleted file mode 100644 index dd161f7..0000000 --- a/JavaScriptCore/kjs/JSWrapperObject.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// -*- 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 deleted file mode 100644 index 0a06c9f..0000000 --- a/JavaScriptCore/kjs/JSWrapperObject.h +++ /dev/null @@ -1,84 +0,0 @@ -// -*- 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/LabelScope.h b/JavaScriptCore/kjs/LabelScope.h new file mode 100644 index 0000000..8c30be5 --- /dev/null +++ b/JavaScriptCore/kjs/LabelScope.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LabelScope_h +#define LabelScope_h + +#include <wtf/PassRefPtr.h> +#include "LabelID.h" + +namespace JSC { + + class Identifier; + + class LabelScope { + public: + enum Type { Loop, Switch, NamedLabel }; + + LabelScope(Type type, const Identifier* name, int scopeDepth, PassRefPtr<LabelID> breakTarget, PassRefPtr<LabelID> continueTarget) + : m_refCount(0) + , m_type(type) + , m_name(name) + , m_scopeDepth(scopeDepth) + , m_breakTarget(breakTarget) + , m_continueTarget(continueTarget) + { + } + + // It doesn't really make sense to copy a LabelScope, but we need this copy + // constructor to support moving LabelScopes in a Vector. + + LabelScope(const LabelScope& other) + : m_refCount(other.m_refCount) + , m_type(other.m_type) + , m_name(other.m_name) + , m_scopeDepth(other.m_scopeDepth) + , m_breakTarget(other.m_breakTarget) + , m_continueTarget(other.m_continueTarget) + { + } + + void ref() { ++m_refCount; } + void deref() + { + --m_refCount; + ASSERT(m_refCount >= 0); + } + int refCount() const { return m_refCount; } + + LabelID* breakTarget() const { return m_breakTarget.get(); } + LabelID* continueTarget() const { return m_continueTarget.get(); } + + Type type() const { return m_type; } + const Identifier* name() const { return m_name; } + int scopeDepth() const { return m_scopeDepth; } + + private: + int m_refCount; + Type m_type; + const Identifier* m_name; + int m_scopeDepth; + RefPtr<LabelID> m_breakTarget; + RefPtr<LabelID> m_continueTarget; + }; + +} // namespace JSC + +#endif // LabelScope_h diff --git a/JavaScriptCore/kjs/LabelStack.h b/JavaScriptCore/kjs/LabelStack.h deleted file mode 100644 index 375edf1..0000000 --- a/JavaScriptCore/kjs/LabelStack.h +++ /dev/null @@ -1,84 +0,0 @@ -// -*- 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 deleted file mode 100644 index 87a54bb..0000000 --- a/JavaScriptCore/kjs/LocalStorage.h +++ /dev/null @@ -1,56 +0,0 @@ -// -*- 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 index 60637f2..2d11dc2 100644 --- a/JavaScriptCore/kjs/NodeInfo.h +++ b/JavaScriptCore/kjs/NodeInfo.h @@ -23,22 +23,41 @@ #include "nodes.h" #include "Parser.h" -namespace KJS { +namespace JSC { -template <typename T> struct NodeInfo { - T m_node; - ParserRefCountedData<DeclarationStacks::VarStack>* m_varDeclarations; - ParserRefCountedData<DeclarationStacks::FunctionStack>* m_funcDeclarations; -}; + template <typename T> struct NodeInfo { + T m_node; + CodeFeatures m_features; + int m_numConstants; + }; -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; + typedef NodeInfo<FuncDeclNode*> FuncDeclNodeInfo; + typedef NodeInfo<FuncExprNode*> FuncExprNodeInfo; + typedef NodeInfo<ExpressionNode*> ExpressionNodeInfo; + typedef NodeInfo<ArgumentsNode*> ArgumentsNodeInfo; + typedef NodeInfo<ConstDeclNode*> ConstDeclNodeInfo; + typedef NodeInfo<PropertyNode*> PropertyNodeInfo; + typedef NodeInfo<PropertyList> PropertyListInfo; + typedef NodeInfo<ElementList> ElementListInfo; + typedef NodeInfo<ArgumentList> ArgumentListInfo; + + template <typename T> struct NodeDeclarationInfo { + T m_node; + ParserRefCountedData<DeclarationStacks::VarStack>* m_varDeclarations; + ParserRefCountedData<DeclarationStacks::FunctionStack>* m_funcDeclarations; + CodeFeatures m_features; + int m_numConstants; + }; + + typedef NodeDeclarationInfo<StatementNode*> StatementNodeInfo; + typedef NodeDeclarationInfo<CaseBlockNode*> CaseBlockNodeInfo; + typedef NodeDeclarationInfo<CaseClauseNode*> CaseClauseNodeInfo; + typedef NodeDeclarationInfo<SourceElements*> SourceElementsInfo; + typedef NodeDeclarationInfo<ClauseList> ClauseListInfo; + typedef NodeDeclarationInfo<ExpressionNode*> VarDeclListInfo; + typedef NodeDeclarationInfo<ConstDeclList> ConstDeclListInfo; + typedef NodeDeclarationInfo<ParameterList> ParameterListInfo; -} // namespace KJS +} // namespace JSC #endif // NodeInfo_h diff --git a/JavaScriptCore/kjs/Parser.cpp b/JavaScriptCore/kjs/Parser.cpp index c6d7265..26773e8 100644 --- a/JavaScriptCore/kjs/Parser.cpp +++ b/JavaScriptCore/kjs/Parser.cpp @@ -1,9 +1,7 @@ -// -*- 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. + * Copyright (C) 2003, 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 @@ -25,67 +23,55 @@ #include "config.h" #include "Parser.h" +#include "Debugger.h" #include "lexer.h" #include <wtf/HashSet.h> #include <wtf/Vector.h> -extern int kjsyyparse(); +extern int kjsyyparse(void*); -namespace KJS { +namespace JSC { -Parser::Parser() - : m_sourceId(0) -{ -} - -void Parser::parse(int startingLineNumber, - const UChar* code, unsigned length, - int* sourceId, int* errLine, UString* errMsg) +void Parser::parse(JSGlobalData* globalData, int* errLine, UString* errMsg) { ASSERT(!m_sourceElements); - if (errLine) - *errLine = -1; - if (errMsg) - *errMsg = 0; - - Lexer& lexer = KJS::lexer(); + int defaultErrLine; + UString defaultErrMsg; + + if (!errLine) + errLine = &defaultErrLine; + if (!errMsg) + errMsg = &defaultErrMsg; + + *errLine = -1; + *errMsg = 0; - lexer.setCode(startingLineNumber, code, length); - m_sourceId++; - if (sourceId) - *sourceId = m_sourceId; + Lexer& lexer = *globalData->lexer; + lexer.setCode(*m_source); - int parseError = kjsyyparse(); + int parseError = kjsyyparse(globalData); bool lexError = lexer.sawError(); lexer.clear(); - ParserRefCounted::deleteNewObjects(); + ParserRefCounted::deleteNewObjects(globalData); if (parseError || lexError) { - if (errLine) - *errLine = lexer.lineNo(); - if (errMsg) - *errMsg = "Parse error"; + *errLine = lexer.lineNo(); + *errMsg = "Parse error"; m_sourceElements.clear(); } } void Parser::didFinishParsing(SourceElements* sourceElements, ParserRefCountedData<DeclarationStacks::VarStack>* varStack, - ParserRefCountedData<DeclarationStacks::FunctionStack>* funcStack, int lastLine) + ParserRefCountedData<DeclarationStacks::FunctionStack>* funcStack, CodeFeatures features, int lastLine, int numConstants) { - m_sourceElements = sourceElements ? sourceElements : new SourceElements; + m_sourceElements = sourceElements; m_varDeclarations = varStack; m_funcDeclarations = funcStack; + m_features = features; m_lastLine = lastLine; + m_numConstants = numConstants; } -Parser& parser() -{ - ASSERT(JSLock::currentThreadIsHoldingLock()); - - static Parser& staticParser = *new Parser; - return staticParser; -} - -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/Parser.h b/JavaScriptCore/kjs/Parser.h index 92548fe..c2f55d7 100644 --- a/JavaScriptCore/kjs/Parser.h +++ b/JavaScriptCore/kjs/Parser.h @@ -1,9 +1,7 @@ -// -*- 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. + * Copyright (C) 2003, 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 @@ -25,75 +23,75 @@ #ifndef Parser_h #define Parser_h +#include "SourceProvider.h" +#include "Debugger.h" +#include "nodes.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> -#include "nodes.h" -namespace KJS { +namespace JSC { class FunctionBodyNode; class ProgramNode; class UString; - struct UChar; + template <typename T> + struct ParserRefCountedData : ParserRefCounted { + ParserRefCountedData(JSGlobalData* globalData) + : ParserRefCounted(globalData) + { + } - 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; } + template <class ParsedNode> PassRefPtr<ParsedNode> parse(ExecState*, Debugger*, const SourceCode&, int* errLine = 0, UString* errMsg = 0); void didFinishParsing(SourceElements*, ParserRefCountedData<DeclarationStacks::VarStack>*, - ParserRefCountedData<DeclarationStacks::FunctionStack>*, int lastLine); + ParserRefCountedData<DeclarationStacks::FunctionStack>*, CodeFeatures features, int lastLine, int numConstants); private: - friend Parser& parser(); + void parse(JSGlobalData*, int* errLine, UString* errMsg); - 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; + const SourceCode* m_source; RefPtr<SourceElements> m_sourceElements; RefPtr<ParserRefCountedData<DeclarationStacks::VarStack> > m_varDeclarations; RefPtr<ParserRefCountedData<DeclarationStacks::FunctionStack> > m_funcDeclarations; + CodeFeatures m_features; int m_lastLine; + int m_numConstants; }; - - 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) + template <class ParsedNode> PassRefPtr<ParsedNode> Parser::parse(ExecState* exec, Debugger* debugger, const SourceCode& source, int* errLine, UString* errMsg) { - m_sourceURL = sourceURL; - parse(startingLineNumber, code, length, sourceId, errLine, errMsg); - if (!m_sourceElements) { - m_sourceURL = UString(); - return 0; + m_source = &source; + parse(&exec->globalData(), errLine, errMsg); + RefPtr<ParsedNode> result; + if (m_sourceElements) { + result = ParsedNode::create(&exec->globalData(), + m_sourceElements.get(), + m_varDeclarations ? &m_varDeclarations->data : 0, + m_funcDeclarations ? &m_funcDeclarations->data : 0, + *m_source, + m_features, + m_numConstants); + result->setLoc(m_source->firstLine(), m_lastLine); } - RefPtr<ParsedNode> node = ParsedNode::create(m_sourceElements.release().get(), - m_varDeclarations ? &m_varDeclarations->data : 0, - m_funcDeclarations ? &m_funcDeclarations->data : 0); + + m_source = 0; + m_sourceElements = 0; m_varDeclarations = 0; m_funcDeclarations = 0; - m_sourceURL = UString(); - node->setLoc(startingLineNumber, m_lastLine); - return node.release(); + + if (debugger) + debugger->sourceParsed(exec, source, *errLine, *errMsg); + return result.release(); } -} // namespace KJS +} // namespace JSC #endif // Parser_h diff --git a/JavaScriptCore/kjs/PropertyNameArray.cpp b/JavaScriptCore/kjs/PropertyNameArray.cpp deleted file mode 100644 index 45dda3a..0000000 --- a/JavaScriptCore/kjs/PropertyNameArray.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -*- 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 deleted file mode 100644 index b702d21..0000000 --- a/JavaScriptCore/kjs/PropertyNameArray.h +++ /dev/null @@ -1,56 +0,0 @@ -// -*- 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/ResultType.h b/JavaScriptCore/kjs/ResultType.h new file mode 100644 index 0000000..f838ce0 --- /dev/null +++ b/JavaScriptCore/kjs/ResultType.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ResultType_h +#define ResultType_h + +namespace JSC { + + struct ResultType { + friend struct OperandTypes; + + typedef char Type; + static const Type TypeReusable = 1; + + static const Type TypeMaybeNumber = 2; + static const Type TypeMaybeString = 4; + static const Type TypeMaybeNull = 8; + static const Type TypeMaybeBool = 16; + static const Type TypeMaybeOther = 32; + + static const Type TypeReusableNumber = 3; + static const Type TypeStringOrReusableNumber = 4; + + explicit ResultType(Type type) + : m_type(type) + { + } + + bool isReusable() + { + return (m_type & TypeReusable); + } + + bool isReusableNumber() + { + return isReusable() && definitelyIsNumber(); + } + + bool definitelyIsNumber() + { + return ((m_type & ~TypeReusable) == TypeMaybeNumber); + } + + bool isNotNumber() + { + return ((m_type & TypeMaybeNumber) == 0); + } + + bool mightBeNumber() + { + return !isNotNumber(); + } + + int toInt() + { + return static_cast<int>(m_type); + } + + static ResultType nullType() + { + return ResultType(TypeMaybeNull); + } + + static ResultType boolean() + { + return ResultType(TypeMaybeBool); + } + + static ResultType constNumber() + { + return ResultType(TypeMaybeNumber); + } + + static ResultType reusableNumber() + { + return ResultType(TypeReusable | TypeMaybeNumber); + } + + static ResultType reusableNumberOrString() + { + return ResultType(TypeReusable | TypeMaybeNumber | TypeMaybeString); + } + + static ResultType string() + { + return ResultType(TypeMaybeString); + } + + static ResultType unknown() + { + return ResultType(TypeMaybeNumber | TypeMaybeString | TypeMaybeNull | TypeMaybeBool | TypeMaybeOther); + } + + static ResultType forAdd(ResultType op1, ResultType op2) + { + if (op1.definitelyIsNumber() && op2.definitelyIsNumber()) + return reusableNumber(); + if (op1.isNotNumber() || op2.isNotNumber()) + return string(); + return reusableNumberOrString(); + } + + private: + Type m_type; + }; + + struct OperandTypes + { + OperandTypes(ResultType first = ResultType::unknown(), ResultType second = ResultType::unknown()) + { + m_u.rds.first = first.m_type; + m_u.rds.second = second.m_type; + } + + union { + struct { + ResultType::Type first; + ResultType::Type second; + } rds; + int i; + } m_u; + + ResultType first() + { + return ResultType(m_u.rds.first); + } + + ResultType second() + { + return ResultType(m_u.rds.second); + } + + int toInt() + { + return m_u.i; + } + static OperandTypes fromInt(int value) + { + OperandTypes types; + types.m_u.i = value; + return types; + } + }; + +} // namespace JSC + +#endif // ResultType_h diff --git a/JavaScriptCore/kjs/SavedBuiltins.h b/JavaScriptCore/kjs/SavedBuiltins.h deleted file mode 100644 index 9901e41..0000000 --- a/JavaScriptCore/kjs/SavedBuiltins.h +++ /dev/null @@ -1,94 +0,0 @@ -// -*- 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/Shell.cpp b/JavaScriptCore/kjs/Shell.cpp new file mode 100644 index 0000000..f35679e --- /dev/null +++ b/JavaScriptCore/kjs/Shell.cpp @@ -0,0 +1,501 @@ +/* + * 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 "CodeGenerator.h" +#include "InitializeThreading.h" +#include "JSArray.h" +#include "JSLock.h" +#include "PrototypeFunction.h" +#include "SamplingTool.h" +#include "completion.h" +#include "interpreter.h" +#include <math.h> +#include <stdio.h> +#include <string.h> + +#if !PLATFORM(WIN_OS) +#include <unistd.h> +#endif + +#if HAVE(READLINE) +#include <readline/history.h> +#include <readline/readline.h> +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if PLATFORM(UNIX) +#include <signal.h> +#endif + +#if COMPILER(MSVC) +#include <crtdbg.h> +#include <windows.h> +#endif + +#if PLATFORM(QT) +#include <QCoreApplication> +#include <QDateTime> +#endif + +using namespace JSC; +using namespace WTF; + +static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer); + +static JSValue* functionPrint(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionDebug(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionGC(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionVersion(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionRun(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionLoad(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionReadline(ExecState*, JSObject*, JSValue*, const ArgList&); +static JSValue* functionQuit(ExecState*, JSObject*, JSValue*, const ArgList&); + +struct Options { + Options() + : interactive(false) + , prettyPrint(false) + , dump(false) + { + } + + bool interactive; + bool prettyPrint; + bool dump; + Vector<UString> fileNames; + Vector<UString> arguments; +}; + +static const char interactivePrompt[] = "> "; +static const UString interpreterName("Interpreter"); + +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(const Vector<UString>& arguments); + virtual UString className() const { return "global"; } +}; +COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false); +ASSERT_CLASS_FITS_IN_CELL(GlobalObject); + +GlobalObject::GlobalObject(const Vector<UString>& arguments) + : JSGlobalObject() +{ + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad)); + putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline)); + + JSObject* array = constructEmptyArray(globalExec()); + for (size_t i = 0; i < arguments.size(); ++i) + array->put(globalExec(), i, jsString(globalExec(), arguments[i])); + putDirect(Identifier(globalExec(), "arguments"), array); +} + +JSValue* functionPrint(ExecState* exec, JSObject*, JSValue*, const ArgList& args) +{ + for (unsigned i = 0; i < args.size(); ++i) { + if (i != 0) + putchar(' '); + + printf("%s", args.at(exec, i)->toString(exec).UTF8String().c_str()); + } + + putchar('\n'); + fflush(stdout); + return jsUndefined(); +} + +JSValue* functionDebug(ExecState* exec, JSObject*, JSValue*, const ArgList& args) +{ + fprintf(stderr, "--> %s\n", args.at(exec, 0)->toString(exec).UTF8String().c_str()); + return jsUndefined(); +} + +JSValue* functionGC(ExecState* exec, JSObject*, JSValue*, const ArgList&) +{ + JSLock lock(false); + exec->heap()->collect(); + return jsUndefined(); +} + +JSValue* functionVersion(ExecState*, JSObject*, JSValue*, const ArgList&) +{ + // 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*, JSValue*, const ArgList& args) +{ + StopWatch stopWatch; + UString fileName = args.at(exec, 0)->toString(exec); + Vector<char> script; + if (!fillBufferWithContentsOfFile(fileName, script)) + return throwError(exec, GeneralError, "Could not open file."); + + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + + stopWatch.start(); + Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName)); + stopWatch.stop(); + + return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS()); +} + +JSValue* functionLoad(ExecState* exec, JSObject*, JSValue*, const ArgList& args) +{ + UString fileName = args.at(exec, 0)->toString(exec); + Vector<char> script; + if (!fillBufferWithContentsOfFile(fileName, script)) + return throwError(exec, GeneralError, "Could not open file."); + + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName)); + + return jsUndefined(); +} + +JSValue* functionReadline(ExecState* exec, JSObject*, JSValue*, const ArgList&) +{ + 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(exec, line.data()); +} + +JSValue* functionQuit(ExecState*, JSObject*, JSValue*, const ArgList&) +{ + 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 jscmain function requires object +// unwinding. + +#if COMPILER(MSVC) && !defined(_DEBUG) +#define TRY __try { +#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; } +#else +#define TRY +#define EXCEPT(x) +#endif + +int jscmain(int argc, char** argv, JSGlobalData*); + +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 + +#if PLATFORM(QT) + QCoreApplication app(argc, argv); +#endif + + int res = 0; + TRY + res = jscmain(argc, argv, JSGlobalData::create().releaseRef()); + EXCEPT(res = 3) + return res; +} + +static bool prettyPrintScript(ExecState* exec, const UString& fileName, const Vector<char>& script) +{ + int errLine = 0; + UString errMsg; + RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), makeSource(script.data(), fileName), &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(GlobalObject* globalObject, const Vector<UString>& fileNames, bool prettyPrint, bool dump) +{ + Vector<char> script; + + if (dump) + CodeGenerator::setDumpsGeneratedCode(true); + +#if ENABLE(OPCODE_SAMPLING) + Machine* machine = globalObject->globalData()->machine; + machine->setSampler(new SamplingTool(machine)); +#endif + + 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(globalObject->globalExec(), fileName, script); + else { +#if ENABLE(OPCODE_SAMPLING) + machine->sampler()->start(); +#endif + Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName)); + success = success && completion.complType() != Throw; + if (dump) { + if (completion.complType() == Throw) + printf("Exception: %s\n", completion.value()->toString(globalObject->globalExec()).ascii()); + else + printf("End: %s\n", completion.value()->toString(globalObject->globalExec()).ascii()); + } + + globalObject->globalExec()->clearException(); + +#if ENABLE(OPCODE_SAMPLING) + machine->sampler()->stop(); +#endif + } + } + +#if ENABLE(OPCODE_SAMPLING) + machine->sampler()->dump(globalObject->globalExec()); + delete machine->sampler(); +#endif + return success; +} + +static void runInteractive(GlobalObject* globalObject) +{ + while (true) { +#if HAVE(READLINE) + char* line = readline(interactivePrompt); + if (!line) + break; + if (line[0]) + add_history(line); + Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName)); + free(line); +#else + puts(interactivePrompt); + 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'); + Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName)); +#endif + if (completion.complType() == Throw) + printf("Exception: %s\n", completion.value()->toString(globalObject->globalExec()).ascii()); + else + printf("%s\n", completion.value()->toString(globalObject->globalExec()).UTF8String().c_str()); + + globalObject->globalExec()->clearException(); + } + printf("\n"); +} + +static void printUsageStatement() +{ + fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n"); + fprintf(stderr, " -d Dumps bytecode (debug builds only)\n"); + fprintf(stderr, " -f Specifies a source file (deprecated)\n"); + fprintf(stderr, " -h|--help Prints this help message\n"); + fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n"); + fprintf(stderr, " -p Prints formatted source code\n"); + fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n"); + exit(-1); +} + +static void parseArguments(int argc, char** argv, Options& options) +{ + int i = 1; + for (; i < argc; ++i) { + const char* arg = argv[i]; + if (strcmp(arg, "-f") == 0) { + if (++i == argc) + printUsageStatement(); + options.fileNames.append(argv[i]); + continue; + } + if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { + printUsageStatement(); + } + if (strcmp(arg, "-i") == 0) { + options.interactive = true; + continue; + } + if (strcmp(arg, "-p") == 0) { + options.prettyPrint = true; + continue; + } + if (strcmp(arg, "-d") == 0) { + options.dump = true; + continue; + } + if (strcmp(arg, "-s") == 0) { +#if PLATFORM(UNIX) + signal(SIGILL, _exit); + signal(SIGFPE, _exit); + signal(SIGBUS, _exit); + signal(SIGSEGV, _exit); +#endif + continue; + } + if (strcmp(arg, "--") == 0) { + ++i; + break; + } + options.fileNames.append(argv[i]); + } + + if (options.fileNames.isEmpty()) + options.interactive = true; + + for (; i < argc; ++i) + options.arguments.append(argv[i]); +} + +int jscmain(int argc, char** argv, JSGlobalData* globalData) +{ + JSC::initializeThreading(); + + JSLock lock(false); + + Options options; + parseArguments(argc, argv, options); + + GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments); + bool success = runWithScripts(globalObject, options.fileNames, options.prettyPrint, options.dump); + if (options.interactive && success) + runInteractive(globalObject); + + 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/SourceCode.h b/JavaScriptCore/kjs/SourceCode.h new file mode 100644 index 0000000..2840161 --- /dev/null +++ b/JavaScriptCore/kjs/SourceCode.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SourceCode_h +#define SourceCode_h + +#include "SourceProvider.h" +#include <wtf/RefPtr.h> + +namespace JSC { + + class SourceCode { + public: + SourceCode() + : m_startChar(0) + , m_endChar(0) + , m_firstLine(0) + { + } + + SourceCode(PassRefPtr<SourceProvider> provider, int firstLine = 1) + : m_provider(provider) + , m_startChar(0) + , m_endChar(m_provider->length()) + , m_firstLine(std::max(firstLine, 1)) + { + } + + SourceCode(PassRefPtr<SourceProvider> provider, int start, int end, int firstLine) + : m_provider(provider) + , m_startChar(start) + , m_endChar(end) + , m_firstLine(std::max(firstLine, 1)) + { + } + + UString toString() const + { + if (!m_provider) + return UString(); + return m_provider->getRange(m_startChar, m_endChar); + } + + bool isNull() const { return !m_provider; } + SourceProvider* provider() const { return m_provider.get(); } + int firstLine() const { return m_firstLine; } + int startOffset() const { return m_startChar; } + const UChar* data() const { return m_provider->data() + m_startChar; } + int length() const { return m_endChar - m_startChar; } + + private: + RefPtr<SourceProvider> m_provider; + int m_startChar; + int m_endChar; + int m_firstLine; + }; + + inline SourceCode makeSource(const UString& source, const UString& url = UString(), int firstLine = 1) + { + return SourceCode(UStringSourceProvider::create(source, url), firstLine); + } + +} // namespace JSC + +#endif // SourceCode_h diff --git a/JavaScriptCore/kjs/SymbolTable.h b/JavaScriptCore/kjs/SourceProvider.h index 1c2b2e8..755a10f 100644 --- a/JavaScriptCore/kjs/SymbolTable.h +++ b/JavaScriptCore/kjs/SourceProvider.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,43 +26,54 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SymbolTable_h -#define SymbolTable_h +#ifndef SourceProvider_h +#define SourceProvider_h #include "ustring.h" -#include <wtf/AlwaysInline.h> +#include <wtf/RefCounted.h> -namespace KJS { +namespace JSC { - 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() + class SourceProvider : public RefCounted<SourceProvider> { + public: + SourceProvider(const UString& url) + : m_url(url) { - return *reinterpret_cast<RefPtr<UString::Rep>*>(&nullRepPtr); } + virtual ~SourceProvider() { } + + virtual UString getRange(int start, int end) const = 0; + virtual const UChar* data() const = 0; + virtual int length() const = 0; + + const UString& url() { return m_url; } + intptr_t asID() { return reinterpret_cast<intptr_t>(this); } private: - static UString::Rep* nullRepPtr; + UString m_url; }; - static ALWAYS_INLINE size_t missingSymbolMarker() { return std::numeric_limits<size_t>::max(); } + class UStringSourceProvider : public SourceProvider { + public: + static PassRefPtr<UStringSourceProvider> create(const UString& source, const UString& url) + { + return adoptRef(new UStringSourceProvider(source, url)); + } - 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; - }; + UString getRange(int start, int end) const { return m_source.substr(start, end - start); } + const UChar* data() const { return m_source.data(); } + int length() const { return m_source.size(); } - typedef HashMap<RefPtr<UString::Rep>, size_t, IdentifierRepHash, IdentifierRepHashTraits, SymbolTableIndexHashTraits> SymbolTable; + private: + UStringSourceProvider(const UString& source, const UString& url) + : SourceProvider(url) + , m_source(source) + { + } -} // namespace KJS + UString m_source; + }; + +} // namespace JSC -#endif // SymbolTable_h +#endif // SourceProvider_h diff --git a/JavaScriptCore/kjs/TypeInfo.h b/JavaScriptCore/kjs/TypeInfo.h new file mode 100644 index 0000000..4f0b16c --- /dev/null +++ b/JavaScriptCore/kjs/TypeInfo.h @@ -0,0 +1,63 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TypeInfo_h +#define TypeInfo_h + +#include "JSType.h" + +namespace JSC { + + // WebCore uses MasqueradesAsUndefined to make document.all and style.filter undetectable. + static const unsigned MasqueradesAsUndefined = 1; + static const unsigned ImplementsHasInstance = 1 << 1; + static const unsigned OverridesHasInstance = 1 << 2; + static const unsigned NeedsThisConversion = 1 << 3; + static const unsigned HasStandardGetOwnPropertySlot = 1 << 4; + + class TypeInfo { + friend class CTI; + public: + TypeInfo(JSType type, unsigned flags = 0) : m_type(type), m_flags(flags) { } + + JSType type() const { return m_type; } + + bool masqueradesAsUndefined() const { return m_flags & MasqueradesAsUndefined; } + bool implementsHasInstance() const { return m_flags & ImplementsHasInstance; } + bool overridesHasInstance() const { return m_flags & OverridesHasInstance; } + bool needsThisConversion() const { return m_flags & NeedsThisConversion; } + bool hasStandardGetOwnPropertySlot() const { return m_flags & HasStandardGetOwnPropertySlot; } + + unsigned flags() const { return m_flags; } + + private: + JSType m_type; + unsigned m_flags; + }; + +} + +#endif // TypeInfo_h diff --git a/JavaScriptCore/kjs/array_instance.cpp b/JavaScriptCore/kjs/array_instance.cpp deleted file mode 100644 index 0578ed7..0000000 --- a/JavaScriptCore/kjs/array_instance.cpp +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007 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, int attributes) -{ - bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); - if (isArrayIndex) { - put(exec, i, value, attributes); - 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, attributes); -} - -void ArrayInstance::put(ExecState* exec, unsigned i, JSValue* value, int attributes) -{ - if (i > maxArrayIndex) { - put(exec, Identifier::from(i), value, attributes); - 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 deleted file mode 100644 index 913d0cd..0000000 --- a/JavaScriptCore/kjs/array_instance.h +++ /dev/null @@ -1,72 +0,0 @@ -// -*- 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*, int attributes = None); - virtual void put(ExecState*, unsigned propertyName, JSValue*, int attributes = None); - 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 deleted file mode 100644 index 48b0855..0000000 --- a/JavaScriptCore/kjs/array_object.cpp +++ /dev/null @@ -1,760 +0,0 @@ -/* - * 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 deleted file mode 100644 index e109c47..0000000 --- a/JavaScriptCore/kjs/array_object.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 deleted file mode 100644 index 10bb738..0000000 --- a/JavaScriptCore/kjs/bool_object.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 deleted file mode 100644 index c3d5a9f..0000000 --- a/JavaScriptCore/kjs/bool_object.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 index f0369c6..53e889e 100644 --- a/JavaScriptCore/kjs/collector.cpp +++ b/JavaScriptCore/kjs/collector.cpp @@ -1,6 +1,5 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * 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 @@ -22,11 +21,15 @@ #include "config.h" #include "collector.h" +#include "ArgList.h" +#include "CollectorHeapIterator.h" #include "ExecState.h" #include "JSGlobalObject.h" -#include "internal.h" -#include "list.h" -#include "value.h" +#include "JSLock.h" +#include "JSString.h" +#include "JSValue.h" +#include "Machine.h" +#include "Tracing.h" #include <algorithm> #include <setjmp.h> #include <stdlib.h> @@ -34,10 +37,6 @@ #include <wtf/HashCountedSet.h> #include <wtf/UnusedParam.h> -#if USE(MULTIPLE_THREADS) -#include <pthread.h> -#endif - #if PLATFORM(DARWIN) #include <mach/mach_port.h> @@ -46,8 +45,6 @@ #include <mach/thread_act.h> #include <mach/vm_map.h> -#include "CollectorHeapIntrospector.h" - #elif PLATFORM(WIN_OS) #include <windows.h> @@ -62,54 +59,139 @@ #include <thread.h> #endif +#if PLATFORM(LINUX) +#include <pthread.h> +#endif + +#if PLATFORM(OPENBSD) +#include <pthread.h> +#endif + #if HAVE(PTHREAD_NP_H) #include <pthread_np.h> -#else -#include <pthread.h> #endif #endif #define DEBUG_COLLECTOR 0 +#define COLLECT_ON_EVERY_ALLOCATION 0 using std::max; -namespace KJS { +namespace JSC { // 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; +// This value has to be a macro to be used in max() without introducing +// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. +#define MIN_ARRAY_SIZE (static_cast<size_t>(14)) -enum OperationInProgress { NoOperation, Allocation, Collection }; +static void freeHeap(CollectorHeap*); -struct CollectorHeap { - CollectorBlock** blocks; - size_t numBlocks; - size_t usedBlocks; - size_t firstBlockWithPossibleSpace; - - size_t numLiveObjects; - size_t numLiveObjectsAtLastCollect; - size_t extraCost; +#if ENABLE(JSC_MULTIPLE_THREADS) - OperationInProgress operationInProgress; +#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 + +class Heap::Thread { +public: + Thread(pthread_t pthread, const PlatformThread& platThread, void* base) + : posixThread(pthread) + , platformThread(platThread) + , stackBase(base) + { + } -static CollectorHeap primaryHeap = { 0, 0, 0, 0, 0, 0, 0, NoOperation }; -static CollectorHeap numberHeap = { 0, 0, 0, 0, 0, 0, 0, NoOperation }; + Thread* next; + pthread_t posixThread; + PlatformThread platformThread; + void* stackBase; +}; -// 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; +#endif -static CollectorBlock* allocateBlock() +Heap::Heap(JSGlobalData* globalData) + : m_markListSet(0) +#if ENABLE(JSC_MULTIPLE_THREADS) + , m_registeredThreads(0) +#endif + , m_globalData(globalData) { -#if PLATFORM(DARWIN) + ASSERT(globalData); + +#if ENABLE(JSC_MULTIPLE_THREADS) + int error = pthread_key_create(&m_currentThreadRegistrar, unregisterThread); + if (error) + CRASH(); +#endif + + memset(&primaryHeap, 0, sizeof(CollectorHeap)); + memset(&numberHeap, 0, sizeof(CollectorHeap)); +} + +Heap::~Heap() +{ + // The destroy function must already have been called, so assert this. + ASSERT(!m_globalData); +} + +void Heap::destroy() +{ + JSLock lock(false); + + if (!m_globalData) + return; + + // The global object is not GC protected at this point, so sweeping may delete it + // (and thus the global data) before other objects that may use the global data. + RefPtr<JSGlobalData> protect(m_globalData); + + delete m_markListSet; + m_markListSet = 0; + + sweep<PrimaryHeap>(); + // No need to sweep number heap, because the JSNumber destructor doesn't do anything. + + ASSERT(!primaryHeap.numLiveObjects); + + freeHeap(&primaryHeap); + freeHeap(&numberHeap); + +#if ENABLE(JSC_MULTIPLE_THREADS) +#ifndef NDEBUG + int error = +#endif + pthread_key_delete(m_currentThreadRegistrar); + ASSERT(!error); + + MutexLocker registeredThreadsLock(m_registeredThreadsMutex); + for (Heap::Thread* t = m_registeredThreads; t;) { + Heap::Thread* next = t->next; + delete t; + t = next; + } +#endif + + m_globalData = 0; +} + +template <HeapType heapType> +static NEVER_INLINE CollectorBlock* allocateBlock() +{ +#if PLATFORM(DARWIN) vm_address_t address = 0; + // FIXME: tag the region as a JavaScriptCore heap when we get a registered VM tag: <rdar://problem/6054788>. 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 @@ -119,8 +201,12 @@ static CollectorBlock* allocateBlock() posix_memalign(&address, BLOCK_SIZE, BLOCK_SIZE); memset(address, 0, BLOCK_SIZE); #else + +#if ENABLE(JSC_MULTIPLE_THREADS) +#error Need to initialize pagesize safely. +#endif static size_t pagesize = getpagesize(); - + size_t extra = 0; if (BLOCK_SIZE > pagesize) extra = BLOCK_SIZE - pagesize; @@ -133,15 +219,15 @@ static CollectorBlock* allocateBlock() adjust = BLOCK_SIZE - (address & BLOCK_OFFSET_MASK); if (adjust > 0) - munmap(reinterpret_cast<void*>(address), adjust); + munmap(reinterpret_cast<char*>(address), adjust); if (adjust < extra) - munmap(reinterpret_cast<void*>(address + adjust + BLOCK_SIZE), extra - adjust); + munmap(reinterpret_cast<char*>(address + adjust + BLOCK_SIZE), extra - adjust); address += adjust; memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE); #endif - + reinterpret_cast<CollectorBlock*>(address)->type = heapType; return reinterpret_cast<CollectorBlock*>(address); } @@ -150,15 +236,24 @@ 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); + VirtualFree(block, 0, MEM_RELEASE); #elif HAVE(POSIX_MEMALIGN) free(block); #else - munmap(block, BLOCK_SIZE); + munmap(reinterpret_cast<char*>(block), BLOCK_SIZE); #endif } -void Collector::recordExtraCost(size_t cost) +static void freeHeap(CollectorHeap* heap) +{ + for (size_t i = 0; i < heap->usedBlocks; ++i) + if (heap->blocks[i]) + freeBlock(heap->blocks[i]); + fastFree(heap->blocks); + memset(heap, 0, sizeof(CollectorHeap)); +} + +void Heap::recordExtraCost(size_t cost) { // Our frequency of garbage collection tries to balance memory use against speed // by collecting based on the number of newly created values. However, for values @@ -175,139 +270,126 @@ void Collector::recordExtraCost(size_t cost) primaryHeap.extraCost += cost; } -template <Collector::HeapType heapType> struct HeapConstants; +template <HeapType heapType> ALWAYS_INLINE void* Heap::heapAllocate(size_t s) +{ + typedef typename HeapConstants<heapType>::Block Block; + typedef typename HeapConstants<heapType>::Cell Cell; -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; -}; + 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 -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; -}; + 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. -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); + size_t numLiveObjects = heap.numLiveObjects; + size_t usedBlocks = heap.usedBlocks; + size_t i = heap.firstBlockWithPossibleSpace; + +#if COLLECT_ON_EVERY_ALLOCATION + collect(); +#endif + + // 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; + // 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 { + Block* targetBlock; + size_t targetBlockUsedCells; + if (i != usedBlocks) { + targetBlock = reinterpret_cast<Block*>(heap.blocks[i]); + targetBlockUsedCells = targetBlock->usedCells; + ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock); + while (targetBlockUsedCells == HeapConstants<heapType>::cellsPerBlock) { + if (++i == usedBlocks) + goto collect; + targetBlock = reinterpret_cast<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; + size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect; + size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect; + const size_t newCost = numNewObjects + heap.extraCost; - if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect) { + if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect) { #ifndef NDEBUG - heap.operationInProgress = NoOperation; + heap.operationInProgress = NoOperation; #endif - bool collected = collect(); + bool collected = collect(); #ifndef NDEBUG - heap.operationInProgress = Allocation; + heap.operationInProgress = Allocation; #endif - if (collected) { - numLiveObjects = heap.numLiveObjects; - usedBlocks = heap.usedBlocks; - i = heap.firstBlockWithPossibleSpace; - goto scan; - } - } + 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 *))); - } + // 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; + targetBlock = reinterpret_cast<Block*>(allocateBlock<heapType>()); + targetBlock->freeList = targetBlock->cells; + targetBlock->heap = this; + targetBlockUsedCells = 0; + heap.blocks[usedBlocks] = reinterpret_cast<CollectorBlock*>(targetBlock); + heap.usedBlocks = usedBlocks + 1; + heap.firstBlockWithPossibleSpace = usedBlocks; + } - // "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; + // 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; + 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; + // FIXME: Consider doing this in NDEBUG builds too (see comment above). + heap.operationInProgress = NoOperation; #endif - return newCell; + return newCell; } -void* Collector::allocate(size_t s) +void* Heap::allocate(size_t s) { return heapAllocate<PrimaryHeap>(s); } -void* Collector::allocateNumber(size_t s) +void* Heap::allocateNumber(size_t s) { return heapAllocate<NumberHeap>(s); } @@ -325,10 +407,10 @@ static inline void* currentThreadStackBase() MOV EAX, FS:[18h] MOV pTib, EAX } - return (void*)pTib->StackBase; + return static_cast<void*>(pTib->StackBase); #elif PLATFORM(WIN_OS) && PLATFORM(X86_64) && COMPILER(MSVC) PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb()); - return (void*)pTib->StackBase; + return reinterpret_cast<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 @@ -336,11 +418,16 @@ static inline void* currentThreadStackBase() asm ( "movl %%fs:0x18, %0\n" : "=r" (pTib) ); - return (void*)pTib->StackBase; + return static_cast<void*>(pTib->StackBase); #elif PLATFORM(SOLARIS) stack_t s; thr_stksegment(&s); return s.ss_sp; +#elif PLATFORM(OPENBSD) + pthread_t thread = pthread_self(); + stack_t stack; + pthread_stackseg_np(thread, &stack); + return stack.ss_sp; #elif PLATFORM(UNIX) static void* stackBase = 0; static size_t stackSize = 0; @@ -368,41 +455,7 @@ static inline void* currentThreadStackBase() #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 +#if ENABLE(JSC_MULTIPLE_THREADS) static inline PlatformThread getCurrentPlatformThread() { @@ -414,136 +467,127 @@ static inline PlatformThread getCurrentPlatformThread() #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) +void Heap::registerThread() { - Collector::Thread* thread = (Collector::Thread*)data; + if (pthread_getspecific(m_currentThreadRegistrar)) + return; - // 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. - } + pthread_setspecific(m_currentThreadRegistrar, this); + Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase()); - JSLock::unlock(); + MutexLocker lock(m_registeredThreadsMutex); - delete thread; + thread->next = m_registeredThreads; + m_registeredThreads = thread; } -static void initializeRegisteredThreadKey() +void Heap::unregisterThread(void* p) { - pthread_key_create(®isteredThreadKey, destroyRegisteredThread); + if (p) + static_cast<Heap*>(p)->unregisterThread(); } -void Collector::registerThread() +void Heap::unregisterThread() { - 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 + pthread_t currentPosixThread = pthread_self(); + + MutexLocker lock(m_registeredThreadsMutex); + + if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) { + Thread* t = m_registeredThreads; + m_registeredThreads = m_registeredThreads->next; + delete t; + } else { + Heap::Thread* last = m_registeredThreads; + Heap::Thread* t; + for (t = m_registeredThreads->next; t; t = t->next) { + if (pthread_equal(t->posixThread, currentPosixThread)) { + last->next = t->next; + break; + } + last = t; + } + ASSERT(t); // If t is NULL, we never found ourselves in the list. + delete t; + } +} - Collector::Thread *thread = new Collector::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase()); +#else // ENABLE(JSC_MULTIPLE_THREADS) - thread->next = registeredThreads; - registeredThreads = thread; - pthread_setspecific(registeredThreadKey, thread); - } +void Heap::registerThread() +{ } #endif -#define IS_POINTER_ALIGNED(p) (((intptr_t)(p) & (sizeof(char *) - 1)) == 0) +#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) +void Heap::markConservatively(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; - } - } + if (start > end) { + void* tmp = start; + start = end; + end = tmp; + } + + ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000); + ASSERT(IS_POINTER_ALIGNED(start)); + ASSERT(IS_POINTER_ALIGNED(end)); + + char** p = static_cast<char**>(start); + char** e = static_cast<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)) { + Heap::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: - ; - } - } + // Mark the primary heap + for (size_t block = 0; block < usedPrimaryBlocks; block++) { + if ((primaryBlocks[block] == blockAddr) & (offset <= lastCellOffset)) { + if (reinterpret_cast<CollectorCell*>(xAsBits)->u.freeCell.zeroIfFree != 0) { + JSCell* imp = reinterpret_cast<JSCell*>(xAsBits); + if (!imp->marked()) + imp->mark(); + } + break; + } + } + endMarkLoop: + ; + } + } } -void Collector::markCurrentThreadConservatively() +void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal() +{ + void* dummy; + void* stackPointer = &dummy; + void* stackBase = currentThreadStackBase(); + markConservatively(stackPointer, stackBase); +} + +void Heap::markCurrentThreadConservatively() { // setjmp forces volatile registers onto the stack jmp_buf registers; @@ -556,43 +600,17 @@ void Collector::markCurrentThreadConservatively() #pragma warning(pop) #endif -#ifdef ANDROID_FIX - // The code is assuming that dummy is after registers on the stack. BUT it is really compiler - // dependent. There is no guaranteed correspondence between the stack order and variables - // declared order. If registers is after dummy in the stack, it won't be marked correctly and - // GC will free variables which should not be. http://bugs.webkit.org/show_bug.cgi?id=16204 - // is caused by this. - // To put dummy in a separate function and declare it as noinline, we ensure registers will be - // marked correctly. - markCurrentThreadConservativelyEx(); -#else - void* dummy; - void* stackPointer = &dummy; - void* stackBase = currentThreadStackBase(); - - markStackObjectsConservatively(stackPointer, stackBase); -#endif -} - -#ifdef ANDROID_FIX -void Collector::markCurrentThreadConservativelyEx() -{ - void* dummy; - void* stackPointer = &dummy; - void* stackBase = currentThreadStackBase(); - - markStackObjectsConservatively(stackPointer, stackBase); + markCurrentThreadConservativelyInternal(); } -#endif -#if USE(MULTIPLE_THREADS) +#if ENABLE(JSC_MULTIPLE_THREADS) static inline void suspendThread(const PlatformThread& platformThread) { #if PLATFORM(DARWIN) - thread_suspend(platformThread); + thread_suspend(platformThread); #elif PLATFORM(WIN_OS) - SuspendThread(platformThread.handle); + SuspendThread(platformThread.handle); #else #error Need a way to suspend threads on this platform #endif @@ -601,9 +619,9 @@ static inline void suspendThread(const PlatformThread& platformThread) static inline void resumeThread(const PlatformThread& platformThread) { #if PLATFORM(DARWIN) - thread_resume(platformThread); + thread_resume(platformThread); #elif PLATFORM(WIN_OS) - ResumeThread(platformThread.handle); + ResumeThread(platformThread.handle); #else #error Need a way to resume threads on this platform #endif @@ -613,14 +631,16 @@ typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit #if PLATFORM(DARWIN) -#if PLATFORM(X86) +#if PLATFORM(X86) typedef i386_thread_state_t PlatformThreadRegisters; -#elif PLATFORM(X86_64) +#elif PLATFORM(X86_64) typedef x86_thread_state64_t PlatformThreadRegisters; -#elif PLATFORM(PPC) +#elif PLATFORM(PPC) typedef ppc_thread_state_t PlatformThreadRegisters; -#elif PLATFORM(PPC64) +#elif PLATFORM(PPC64) typedef ppc_thread_state64_t PlatformThreadRegisters; +#elif PLATFORM(ARM) +typedef arm_thread_state_t PlatformThreadRegisters; #else #error Unknown Architecture #endif @@ -635,35 +655,38 @@ size_t getPlatformThreadRegisters(const PlatformThread& platformThread, Platform { #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; +#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; +#elif PLATFORM(ARM) + unsigned user_count = ARM_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = ARM_THREAD_STATE; #else #error Unknown Architecture #endif - kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); - if (result != KERN_SUCCESS) { - WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, - "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result); - CRASH(); - } - return user_count * sizeof(usword_t); + 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); + 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 @@ -676,11 +699,13 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) #if __DARWIN_UNIX03 #if PLATFORM(X86) - return (void*)regs.__esp; + return reinterpret_cast<void*>(regs.__esp); #elif PLATFORM(X86_64) - return (void*)regs.__rsp; + return reinterpret_cast<void*>(regs.__rsp); #elif PLATFORM(PPC) || PLATFORM(PPC64) - return (void*)regs.__r1; + return reinterpret_cast<void*>(regs.__r1); +#elif PLATFORM(ARM) + return reinterpret_cast<void*>(regs.__sp); #else #error Unknown Architecture #endif @@ -688,11 +713,11 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) #else // !__DARWIN_UNIX03 #if PLATFORM(X86) - return (void*)regs.esp; + return reinterpret_cast<void*>(regs.esp); #elif PLATFORM(X86_64) - return (void*)regs.rsp; + return reinterpret_cast<void*>(regs.rsp); #elif (PLATFORM(PPC) || PLATFORM(PPC64)) - return (void*)regs.r1; + return reinterpret_cast<void*>(regs.r1); #else #error Unknown Architecture #endif @@ -701,156 +726,139 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) // end PLATFORM(DARWIN) #elif PLATFORM(X86) && PLATFORM(WIN_OS) - return (void*)(uintptr_t)regs.Esp; + return reinterpret_cast<void*>((uintptr_t) regs.Esp); #else #error Need a way to get the stack pointer for another thread on this platform #endif } -void Collector::markOtherThreadConservatively(Thread* thread) +void Heap::markOtherThreadConservatively(Thread* thread) { - suspendThread(thread->platformThread); + suspendThread(thread->platformThread); + + PlatformThreadRegisters regs; + size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); - PlatformThreadRegisters regs; - size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); + // mark the thread's registers + markConservatively(static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize)); - // mark the thread's registers - markStackObjectsConservatively((void*)®s, (void*)((char*)®s + regSize)); - - void* stackPointer = otherThreadStackPointer(regs); - markStackObjectsConservatively(stackPointer, thread->stackBase); + void* stackPointer = otherThreadStackPointer(regs); + markConservatively(stackPointer, thread->stackBase); - resumeThread(thread->platformThread); + resumeThread(thread->platformThread); } #endif -void Collector::markStackObjectsConservatively() +void Heap::markStackObjectsConservatively() { - markCurrentThreadConservatively(); + markCurrentThreadConservatively(); + +#if ENABLE(JSC_MULTIPLE_THREADS) + + if (m_currentThreadRegistrar) { + + MutexLocker lock(m_registeredThreadsMutex); -#if USE(MULTIPLE_THREADS) - for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) { - if (!pthread_equal(thread->posixThread, pthread_self())) { - markOtherThreadConservatively(thread); +#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 + // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held, + // and since this is a shared heap, they are real locks. + for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { + if (!pthread_equal(thread->posixThread, pthread_self())) + markOtherThreadConservatively(thread); + } +#ifndef NDEBUG + fastMallocAllow(); +#endif } - } #endif } -typedef HashCountedSet<JSCell*> ProtectCountSet; - -static ProtectCountSet& protectedValues() +void Heap::setGCProtectNeedsLocking() { - static ProtectCountSet staticProtectCountSet; - return staticProtectCountSet; + // Most clients do not need to call this, with the notable exception of WebCore. + // Clients that use shared heap have JSLock protection, while others are supposed + // to do explicit locking. WebCore violates this contract in Database code, + // which calls gcUnprotect from a secondary thread. + if (!m_protectedValuesMutex) + m_protectedValuesMutex.set(new Mutex); } -void Collector::protect(JSValue *k) +void Heap::protect(JSValue* k) { ASSERT(k); - ASSERT(JSLock::lockCount() > 0); - ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance); if (JSImmediate::isImmediate(k)) - return; + return; + + if (m_protectedValuesMutex) + m_protectedValuesMutex->lock(); + + m_protectedValues.add(k->asCell()); - protectedValues().add(k->asCell()); + if (m_protectedValuesMutex) + m_protectedValuesMutex->unlock(); } -void Collector::unprotect(JSValue *k) +void Heap::unprotect(JSValue* k) { ASSERT(k); - ASSERT(JSLock::lockCount() > 0); - ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance); if (JSImmediate::isImmediate(k)) - return; - - protectedValues().remove(k->asCell()); -} + return; -void Collector::collectOnMainThreadOnly(JSValue* value) -{ - ASSERT(value); - ASSERT(JSLock::lockCount() > 0); - ASSERT(JSLock::currentThreadIsHoldingLock()); + if (m_protectedValuesMutex) + m_protectedValuesMutex->lock(); - if (JSImmediate::isImmediate(value)) - return; + m_protectedValues.remove(k->asCell()); - JSCell* cell = value->asCell(); - cellBlock(cell)->collectOnMainThreadOnly.set(cellOffset(cell)); - ++mainThreadOnlyObjectCount; + if (m_protectedValuesMutex) + m_protectedValuesMutex->unlock(); } -void Collector::markProtectedObjects() +Heap* Heap::heap(JSValue* v) { - 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(); - } + if (JSImmediate::isImmediate(v)) + return 0; + return Heap::cellBlock(v->asCell())->heap; } -void Collector::markMainThreadOnlyObjects() +void Heap::markProtectedObjects() { -#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; - } - } - } + if (m_protectedValuesMutex) + m_protectedValuesMutex->lock(); + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { + JSCell* val = it->first; + if (!val->marked()) + val->mark(); } + + if (m_protectedValuesMutex) + m_protectedValuesMutex->unlock(); } -template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThreadIsMainThread) +template <HeapType heapType> size_t Heap::sweep() { 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; + CollectorHeap& heap = heapType == 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]; + Block* curBlock = reinterpret_cast<Block*>(heap.blocks[block]); size_t usedCells = curBlock->usedCells; Cell* freeList = curBlock->freeList; @@ -861,7 +869,7 @@ template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThre if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) { Cell* cell = curBlock->cells + i; - if (heapType != Collector::NumberHeap) { + if (heapType != 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 @@ -869,11 +877,6 @@ template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThre 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(); } @@ -889,18 +892,13 @@ template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThre } else { size_t minimumCellsToProcess = usedCells; for (size_t i = 0; (i < minimumCellsToProcess) & (i < HeapConstants<heapType>::cellsPerBlock); i++) { - Cell *cell = curBlock->cells + 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; - } + if (heapType != NumberHeap) { + JSCell* imp = reinterpret_cast<JSCell*>(cell); imp->~JSCell(); } --usedCells; @@ -923,7 +921,7 @@ template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThre emptyBlocks++; if (emptyBlocks > SPARE_EMPTY_BLOCKS) { #if !DEBUG_COLLECTOR - freeBlock((CollectorBlock*)curBlock); + freeBlock(reinterpret_cast<CollectorBlock*>(curBlock)); #endif // swap with the last block so we compact as we go heap.blocks[block] = heap.blocks[heap.usedBlocks - 1]; @@ -932,7 +930,7 @@ template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThre 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 *)); + heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock*))); } } } @@ -947,145 +945,150 @@ template <Collector::HeapType heapType> size_t Collector::sweep(bool currentThre return numLiveObjects; } -bool Collector::collect() +bool Heap::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; +#ifndef NDEBUG + if (m_globalData->isSharedInstance) { + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + } +#endif - bool currentThreadIsMainThread = onMainThread(); + ASSERT((primaryHeap.operationInProgress == NoOperation) | (numberHeap.operationInProgress == NoOperation)); + if ((primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation)) + abort(); + + JAVASCRIPTCORE_GC_BEGIN(); + primaryHeap.operationInProgress = Collection; + numberHeap.operationInProgress = Collection; + + // MARK: first mark all referenced objects recursively starting out from the set of root objects + + markStackObjectsConservatively(); + markProtectedObjects(); + if (m_markListSet && m_markListSet->size()) + ArgList::markLists(*m_markListSet); + if (m_globalData->exception && !m_globalData->exception->marked()) + m_globalData->exception->mark(); + m_globalData->machine->registerFile().markCallFrames(this); + m_globalData->smallStrings.mark(); + + JSGlobalObject* globalObject = m_globalData->head; + if (globalObject) { + do { + globalObject->markCrossHeapDependentObjects(); + globalObject = globalObject->next(); + } while (globalObject != m_globalData->head); + } - // MARK: first mark all referenced objects recursively starting out from the set of root objects + JAVASCRIPTCORE_GC_MARKED(); -#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 + size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects; + size_t numLiveObjects = sweep<PrimaryHeap>(); + numLiveObjects += sweep<NumberHeap>(); - markStackObjectsConservatively(); - markProtectedObjects(); - ExecState::markActiveExecStates(); - List::markProtectedLists(); -#if USE(MULTIPLE_THREADS) - if (!currentThreadIsMainThread) - markMainThreadOnlyObjects(); -#endif + primaryHeap.operationInProgress = NoOperation; + numberHeap.operationInProgress = NoOperation; + JAVASCRIPTCORE_GC_END(originalLiveObjects, numLiveObjects); -#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; + return numLiveObjects < originalLiveObjects; } -size_t Collector::size() +size_t Heap::size() { - return primaryHeap.numLiveObjects + numberHeap.numLiveObjects; + return primaryHeap.numLiveObjects + numberHeap.numLiveObjects; } -size_t Collector::globalObjectCount() +size_t Heap::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 count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + ++count; + o = o->next(); + } while (o != head); + } + return count; } -size_t Collector::protectedGlobalObjectCount() +size_t Heap::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; + if (m_protectedValuesMutex) + m_protectedValuesMutex->lock(); + + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + if (m_protectedValues.contains(o)) + ++count; + o = o->next(); + } while (o != head); + } + + if (m_protectedValuesMutex) + m_protectedValuesMutex->unlock(); + + return count; } -size_t Collector::protectedObjectCount() +size_t Heap::protectedObjectCount() { - return protectedValues().size(); + if (m_protectedValuesMutex) + m_protectedValuesMutex->lock(); + + size_t result = m_protectedValues.size(); + + if (m_protectedValuesMutex) + m_protectedValuesMutex->unlock(); + + return result; } -static const char *typeName(JSCell *val) +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; + if (val->isString()) + return "string"; + if (val->isNumber()) + return "number"; + if (val->isGetterSetter()) + return "gettersetter"; + ASSERT(val->isObject()); + const ClassInfo* info = static_cast<JSObject*>(val)->classInfo(); + return info ? info->className : "Object"; } -HashCountedSet<const char*>* Collector::protectedObjectTypeCounts() +HashCountedSet<const char*>* Heap::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) + if (m_protectedValuesMutex) + m_protectedValuesMutex->lock(); + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) counts->add(typeName(it->first)); + if (m_protectedValuesMutex) + m_protectedValuesMutex->unlock(); + return counts; } -bool Collector::isBusy() +bool Heap::isBusy() { return (primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation); } -void Collector::reportOutOfMemoryToAllExecStates() +Heap::iterator Heap::primaryHeapBegin() { - 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")); - } + return iterator(primaryHeap.blocks, primaryHeap.blocks + primaryHeap.usedBlocks); +} + +Heap::iterator Heap::primaryHeapEnd() +{ + return iterator(primaryHeap.blocks + primaryHeap.usedBlocks, primaryHeap.blocks + primaryHeap.usedBlocks); } -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/collector.h b/JavaScriptCore/kjs/collector.h index 3068d00..4233b06 100644 --- a/JavaScriptCore/kjs/collector.h +++ b/JavaScriptCore/kjs/collector.h @@ -1,9 +1,7 @@ -// -*- 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. + * 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 @@ -24,180 +22,258 @@ #ifndef KJSCOLLECTOR_H_ #define KJSCOLLECTOR_H_ +#include "JSImmediate.h" #include <string.h> #include <wtf/HashCountedSet.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/Threading.h> -#ifdef ANDROID_FIX -#if COMPILER(GCC) -#define KJS_NO_INLINE __attribute__((noinline)) -#else -#define KJS_NO_INLINE +// This is supremely lame that we require pthreads to build on windows. +#if ENABLE(JSC_MULTIPLE_THREADS) +#include <pthread.h> #endif -#endif // ANDROID_FIX -namespace KJS { +#define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) - class JSCell; - class JSValue; - class CollectorBlock; +namespace JSC { - 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 + class ArgList; + class CollectorBlock; + class JSCell; + class JSGlobalData; - static const size_t minExtraCostSize = 256; + enum OperationInProgress { NoOperation, Allocation, Collection }; + enum HeapType { PrimaryHeap, NumberHeap }; - static void reportExtraMemoryCost(size_t cost); + template <HeapType> class CollectorHeapIterator; - static size_t size(); + struct CollectorHeap { + CollectorBlock** blocks; + size_t numBlocks; + size_t usedBlocks; + size_t firstBlockWithPossibleSpace; - static void protect(JSValue*); - static void unprotect(JSValue*); - - static void collectOnMainThreadOnly(JSValue*); + size_t numLiveObjects; + size_t numLiveObjectsAtLastCollect; + size_t extraCost; - static size_t globalObjectCount(); - static size_t protectedObjectCount(); - static size_t protectedGlobalObjectCount(); - static HashCountedSet<const char*>* protectedObjectTypeCounts(); + OperationInProgress operationInProgress; + }; - class Thread; - static void registerThread(); - - static void registerAsMainThread(); + class Heap : Noncopyable { + public: + class Thread; + typedef CollectorHeapIterator<PrimaryHeap> iterator; - static bool isCellMarked(const JSCell*); - static void markCell(JSCell*); + void destroy(); - enum HeapType { PrimaryHeap, NumberHeap }; +#ifdef JAVASCRIPTCORE_BUILDING_ALL_IN_ONE_FILE + // We can inline these functions because everything is compiled as + // one file, so the heapAllocate template definitions are available. + // However, allocateNumber is used via jsNumberCell outside JavaScriptCore. + // Thus allocateNumber needs to provide a non-inline version too. + void* inlineAllocateNumber(size_t s) { return heapAllocate<NumberHeap>(s); } + void* inlineAllocate(size_t s) { return heapAllocate<PrimaryHeap>(s); } +#endif + void* allocateNumber(size_t); + void* allocate(size_t); + + bool collect(); + bool isBusy(); // true if an allocation or collection is in progress + + static const size_t minExtraCostSize = 256; + + void reportExtraMemoryCost(size_t cost); + + size_t size(); + + void setGCProtectNeedsLocking(); + void protect(JSValue*); + void unprotect(JSValue*); + + static Heap* heap(JSValue*); // 0 for immediate values + + size_t globalObjectCount(); + size_t protectedObjectCount(); + size_t protectedGlobalObjectCount(); + HashCountedSet<const char*>* protectedObjectTypeCounts(); + + void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. + + static bool isCellMarked(const JSCell*); + static void markCell(JSCell*); + + void markConservatively(void* start, void* end); + + HashSet<ArgList*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<ArgList*>; return *m_markListSet; } + + JSGlobalData* globalData() const { return m_globalData; } + static bool isNumber(JSCell*); + + // Iterators for the object heap. + iterator primaryHeapBegin(); + iterator primaryHeapEnd(); + + private: + template <HeapType heapType> void* heapAllocate(size_t); + template <HeapType heapType> size_t sweep(); + static CollectorBlock* cellBlock(const JSCell*); + static size_t cellOffset(const JSCell*); + + friend class JSGlobalData; + Heap(JSGlobalData*); + ~Heap(); + + void recordExtraCost(size_t); + void markProtectedObjects(); + void markCurrentThreadConservatively(); + void markCurrentThreadConservativelyInternal(); + void markOtherThreadConservatively(Thread*); + void markStackObjectsConservatively(); + + typedef HashCountedSet<JSCell*> ProtectCountSet; + + CollectorHeap primaryHeap; + CollectorHeap 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(); -#ifdef ANDROID_FIX - static void markCurrentThreadConservativelyEx() KJS_NO_INLINE; + OwnPtr<Mutex> m_protectedValuesMutex; // Only non-null if the client explicitly requested it via setGCPrtotectNeedsLocking(). + ProtectCountSet m_protectedValues; + + HashSet<ArgList*>* m_markListSet; + +#if ENABLE(JSC_MULTIPLE_THREADS) + static void unregisterThread(void*); + void unregisterThread(); + + Mutex m_registeredThreadsMutex; + Thread* m_registeredThreads; + pthread_key_t m_currentThreadRegistrar; #endif - 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); + + JSGlobalData* m_globalData; + }; + + // 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 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; - }; - - 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 + 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; + Heap* heap; + HeapType type; + }; + + class SmallCellCollectorBlock { + public: + SmallCollectorCell cells[SMALL_CELLS_PER_BLOCK]; + uint32_t usedCells; + SmallCollectorCell* freeList; + CollectorBitmap marked; + Heap* heap; + HeapType type; + }; + + template <HeapType heapType> struct HeapConstants; + + template <> struct HeapConstants<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<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; + }; + + inline CollectorBlock* Heap::cellBlock(const JSCell* cell) + { + return reinterpret_cast<CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); + } + + inline bool Heap::isNumber(JSCell* cell) + { + return Heap::cellBlock(cell)->type == NumberHeap; + } + + inline size_t Heap::cellOffset(const JSCell* cell) + { + return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; + } + + inline bool Heap::isCellMarked(const JSCell* cell) + { + return cellBlock(cell)->marked.get(cellOffset(cell)); + } + + inline void Heap::markCell(JSCell* cell) + { + cellBlock(cell)->marked.set(cellOffset(cell)); + } + + inline void Heap::reportExtraMemoryCost(size_t cost) + { + if (cost > minExtraCostSize) + recordExtraCost(cost / (CELL_SIZE * 2)); + } + +} // namespace JSC #endif /* KJSCOLLECTOR_H_ */ diff --git a/JavaScriptCore/kjs/completion.h b/JavaScriptCore/kjs/completion.h index 09f3db7..56d13ed 100644 --- a/JavaScriptCore/kjs/completion.h +++ b/JavaScriptCore/kjs/completion.h @@ -1,4 +1,3 @@ -// -*- c-basic-offset: 2 -*- /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) @@ -24,37 +23,34 @@ #ifndef KJS_COMPLETION_H #define KJS_COMPLETION_H -namespace KJS { +#include "JSValue.h" - class JSValue; +namespace JSC { - enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted }; + 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) { } + /* + * Completion objects are used to convey the return status and value + * from functions. + */ + class Completion { + public: + Completion(ComplType type = Normal, JSValue* value = noValue()) + : 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; } + 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; - }; + private: + ComplType m_type; + JSValue* m_value; + }; -} +} // namespace JSC -#endif +#endif // KJS_COMPLETION_H diff --git a/JavaScriptCore/kjs/config.h b/JavaScriptCore/kjs/config.h index fd2779b..80a3798 100644 --- a/JavaScriptCore/kjs/config.h +++ b/JavaScriptCore/kjs/config.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 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 @@ -19,40 +19,19 @@ * */ -#include <wtf/Platform.h> - -#if PLATFORM(MAC) -#define HAVE_JNI 1 +#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H +#include "autotoolsconfig.h" #endif -#ifdef ANDROID +#include <wtf/Platform.h> -#define HAVE_JNI 1 -#define HAVE_FUNC_FINITE 1 -#define HAVE_MMAP 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_ERRNO_H 1 -#define HAVE_SBRK 1 +#if PLATFORM(ANDROID) #define ANDROID_MOBILE // change can be merged back to WebKit.org for MOBILE //#define ANDROID_INSTRUMENT +#endif -#define ANDROID_FIX // changes can be merged back, or already in, WebKit.org's ToT - -#elif 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 PLATFORM(WIN_OS) // If we don't define these, they get defined in windef.h. // We want to use std::min and std::max @@ -66,24 +45,9 @@ #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) +#if PLATFORM(FREEBSD) || PLATFORM(OPENBSD) #define HAVE_PTHREAD_NP_H 1 #endif diff --git a/JavaScriptCore/kjs/create_hash_table b/JavaScriptCore/kjs/create_hash_table index 6800722..829a024 100755 --- a/JavaScriptCore/kjs/create_hash_table +++ b/JavaScriptCore/kjs/create_hash_table @@ -40,66 +40,80 @@ 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 @values = (); my @hashes = (); -my @table = (); -my @links = (); my $inside = 0; my $name; my $size; -my $hashSizeMask; my $banner = 0; -sub calcTable(); +sub calcSize(); sub output(); +sub jsc_ucfirst($); 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"; - } + chomp; + s/^\s+//; + next if /^\#|^$/; # Comment or blank line. Do nothing. + if (/^\@begin/ && !$inside) { + if (/^\@begin\s*([:_\w]+)\s*\d*\s*$/) { + $inside = 1; + $name = $1; + } else { + print STDERR "WARNING: \@begin without table name, skipping $_\n"; + } } elsif (/^\@end\s*$/ && $inside) { + calcSize(); + output(); - calcTable(); - - output(); - @keys = (); - @values = (); - @attrs = (); - @params = (); - @table = (); - @links = (); - @hashes = (); - $inside = 0; + @keys = (); + @attrs = (); + @values = (); + @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"); + my $key = $1; + my $val = $2; + my $att = $3; + my $param = $4; + + push(@keys, $key); + push(@attrs, length($att) > 0 ? $att : "0"); + + if ($att =~ m/Function/) { + push(@values, { "type" => "Function", "function" => $val, "params" => (length($param) ? $param : "") }); + #printf STDERR "WARNING: Number of arguments missing for $key/$val\n" if (length($param) == 0); + } elsif (length($att)) { + my $get = $val; + my $put = !($att =~ m/ReadOnly/) ? "set" . jsc_ucfirst($val) : "0"; + push(@values, { "type" => "Property", "get" => $get, "put" => $put }); + } else { + push(@values, { "type" => "Lexer", "value" => $val }); + } + push(@hashes, hashValue($key)); } elsif ($inside) { - die "invalid data {" . $_ . "}"; + die "invalid data {" . $_ . "}"; } } die "missing closing \@end" if ($inside); +sub jsc_ucfirst($) +{ + my ($value) = @_; + + if ($value =~ /js/) { + $value =~ s/js/JS/; + return $value; + } + + return ucfirst($value); +} + + sub ceilingToPowerOf2 { my ($size) = @_; @@ -112,36 +126,18 @@ sub ceilingToPowerOf2 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++; - } +sub calcSize() +{ +tableSizeLoop: + for ($size = ceilingToPowerOf2(scalar @keys); ; $size += $size) { + my @table = (); + foreach my $key (@keys) { + my $h = hashValue($key) % $size; + next tableSizeLoop if $table[$h]; + $table[$h] = 1; + } + last; } - $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($$) { @@ -203,48 +199,44 @@ sub hashValue($) { } sub output() { - if (!$banner) { - $banner = 1; - print "/* Automatically generated from $file using $0. DO NOT EDIT ! */\n"; - } + if (!$banner) { + $banner = 1; + print "// Automatically generated from $file using $0. DO NOT EDIT!\n"; + } - my $nameEntries = "${name}Entries"; - $nameEntries =~ s/:/_/g; + my $nameEntries = "${name}Values"; + $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 "\n#include \"lookup.h\"\n" if ($includelookup); + if ($useNameSpace) { + print "\nnamespace ${useNameSpace} {\n"; + print "\nusing namespace JSC;\n"; + } else { + print "\nnamespace JSC {\n"; + } + my $count = scalar @keys + 1; + print "\nstatic const struct HashTableValue ${nameEntries}\[$count\] = {\n"; + my $i = 0; + foreach my $key (@keys) { + my $firstValue = ""; + my $secondValue = ""; + + if ($values[$i]{"type"} eq "Function") { + $firstValue = $values[$i]{"function"}; + $secondValue = $values[$i]{"params"}; + } elsif ($values[$i]{"type"} eq "Property") { + $firstValue = $values[$i]{"get"}; + $secondValue = $values[$i]{"put"}; + } elsif ($values[$i]{"type"} eq "Lexer") { + $firstValue = $values[$i]{"value"}; + $secondValue = "0"; } - print "/* " . $hashes[$entry] . " */ "; - } else { - print " { 0, { 0 }, 0, 0, 0 }"; - } - print "," unless ($i == $size - 1); - print "\n"; - $i++; + print " { \"$key\", $attrs[$i], (intptr_t)$firstValue, (intptr_t)$secondValue },\n"; + $i++; } - - print "};\n\n"; - print "const struct HashTable $name = "; - print "\{ 3, $size, $nameEntries, $hashSizeMask \};\n\n"; - print "} // namespace\n"; + print " { 0, 0, 0, 0 }\n"; + print "};\n\n"; + print "extern const struct HashTable $name = "; + print "\{ ", $size - 1, ", $nameEntries, 0 \};\n\n"; + print "} // namespace\n"; } diff --git a/JavaScriptCore/kjs/date_object.cpp b/JavaScriptCore/kjs/date_object.cpp deleted file mode 100644 index 6b757c2..0000000 --- a/JavaScriptCore/kjs/date_object.cpp +++ /dev/null @@ -1,1615 +0,0 @@ -/* - * 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 void fillStructuresUsingTimeArgs(ExecState* exec, const List& args, int maxArgs, double* ms, GregorianDateTime* t) -{ - double milliseconds = 0; - 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) * msPerHour; - } - - // minutes - if (maxArgs >= 3 && idx < numArgs) { - t->minute = 0; - milliseconds += args[idx++]->toInt32(exec) * msPerMinute; - } - - // seconds - if (maxArgs >= 2 && idx < numArgs) { - t->second = 0; - milliseconds += args[idx++]->toInt32(exec) * msPerSecond; - } - - // milliseconds - if (idx < numArgs) - milliseconds += args[idx]->toNumber(exec); - else - milliseconds += *ms; - - *ms = milliseconds; -} - -// 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 void fillStructuresUsingDateArgs(ExecState *exec, const List &args, int maxArgs, double *ms, GregorianDateTime *t) -{ - int idx = 0; - 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) - 1900; - - // months - if (maxArgs >= 2 && idx < numArgs) - t->month = args[idx++]->toInt32(exec); - - // days - if (idx < numArgs) { - t->monthDay = 0; - *ms += args[idx]->toInt32(exec) * msPerDay; - } -} - -// ------------------------------ 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; - memset(&t, 0, sizeof(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; - memset(&t, 0, sizeof(tm)); - 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); - double secs = floor(milli / msPerSecond); - double ms = milli - secs * msPerSecond; - - GregorianDateTime t; - msToGregorianDateTime(milli, inputIsUTC, t); - - fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &t); - - 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); - JSValue* v = thisDateObj->internalValue(); - double milli = v->toNumber(exec); - double secs = floor(milli / msPerSecond); - double ms = milli - secs * msPerSecond; - - GregorianDateTime t; - msToGregorianDateTime(milli, inputIsUTC, t); - - fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &t); - - 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); - JSValue* v = thisDateObj->internalValue(); - double milli = v->toNumber(exec); - double secs = floor(milli / msPerSecond); - double ms = milli - secs * msPerSecond; - - GregorianDateTime t; - msToGregorianDateTime(milli, utc, t); - - t.year = (args[0]->toInt32(exec) > 99 || args[0]->toInt32(exec) < 0) ? args[0]->toInt32(exec) - 1900 : args[0]->toInt32(exec); - - 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); - - // IE returns the full year even in getYear. - if (exec->dynamicGlobalObject()->compatMode() == IECompat) - return jsNumber(1900 + t.year); - return jsNumber(t.year); -} - -} // namespace KJS diff --git a/JavaScriptCore/kjs/date_object.h b/JavaScriptCore/kjs/date_object.h deleted file mode 100644 index c545980..0000000 --- a/JavaScriptCore/kjs/date_object.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 deleted file mode 100644 index af9c5fa..0000000 --- a/JavaScriptCore/kjs/debugger.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// -*- 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 deleted file mode 100644 index 2d5cb6f..0000000 --- a/JavaScriptCore/kjs/debugger.h +++ /dev/null @@ -1,225 +0,0 @@ -// -*- 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 index bee701a..2dc2cff 100644 --- a/JavaScriptCore/kjs/dtoa.cpp +++ b/JavaScriptCore/kjs/dtoa.cpp @@ -3,6 +3,7 @@ * The author of this software is David M. Gay. * * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * Copyright (C) 2002, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice @@ -18,26 +19,26 @@ ***************************************************************/ /* 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 + 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); + * _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. +/* strtod for IEEE-arithmetic machines. * * This strtod returns a nearest machine number to the input decimal * string (or sets errno to ERANGE). With IEEE arithmetic, ties are @@ -49,128 +50,102 @@ * * 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). + * 1. We only require IEEE. + * 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. + * 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). + * significant byte has the lowest address. * #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. + * computation of dtoa. * #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. + * and Honor_FLT_ROUNDS is not #defined. * #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. + * products but inaccurate quotients, e.g., for Intel i860. + * #define USE_LONG_LONG on machines that have a "long long" + * integer type (of >= 64 bits), and performance testing shows that + * it is faster than 32-bit fallback (which is often not the case + * on 32-bit machines). On such machines, you can #define Just_16 + * to store 16 bits per 32-bit int32_t when doing high-precision integer + * arithmetic. Whether this speeds things up or slows things down + * depends on the machine and the number being converted. * #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 some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. * #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. + * 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 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. + * 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). + * 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). + * 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. + * the result overflows to +-Infinity or underflows to 0. */ #include "config.h" #include "dtoa.h" +#include <errno.h> +#include <float.h> +#include <math.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <wtf/AlwaysInline.h> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/Threading.h> + #if COMPILER(MSVC) #pragma warning(disable: 4244) #pragma warning(disable: 4245) @@ -182,124 +157,34 @@ // http://lists.debian.org/debian-arm/2003/11/msg00008.html #if PLATFORM(BIG_ENDIAN) || PLATFORM(MIDDLE_ENDIAN) #define IEEE_MC68k +#elif PLATFORM(MIDDLE_ENDIAN) +#define IEEE_ARM #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 +#define INFNAN_CHECK -#ifdef __cplusplus -extern "C" { +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(IEEE_ARM) != 1 +Exactly one of IEEE_8087, IEEE_ARM or IEEE_MC68k should be defined. #endif -#ifndef CONST_ -#ifdef KR_headers -#define CONST_ /* blank */ -#else -#define CONST_ const -#endif -#endif +namespace JSC { -#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. +#if ENABLE(JSC_MULTIPLE_THREADS) +Mutex* s_dtoaP5Mutex; #endif -typedef union { double d; ULong L[2]; } U; +typedef union { double d; uint32_t 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] +#define word0(x) ((uint32_t*)&x)[1] +#define word1(x) ((uint32_t*)&x)[0] #else -#define word0(x) ((ULong *)&x)[0] -#define word1(x) ((ULong *)&x)[1] +#define word0(x) ((uint32_t*)&x)[0] +#define word1(x) ((uint32_t*)&x)[1] #endif #else #ifdef IEEE_8087 @@ -316,21 +201,12 @@ typedef union { double d; ULong L[2]; } U; * 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++) +#if defined(IEEE_8087) || defined(IEEE_ARM) +#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++) +#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 @@ -355,1099 +231,780 @@ typedef union { double d; ULong L[2]; } U; #define Tiny1 1 #define Quick_max 14 #define Int_max 14 -#ifndef NO_IEEE_Scale + +#if !defined(NO_IEEE_Scale) +#undef Avoid_Underflow #define Avoid_Underflow -#ifdef Flush_Denorm /* debugging option */ -#undef Sudden_Underflow -#endif #endif -#ifndef Flt_Rounds -#ifdef FLT_ROUNDS +#if !defined(Flt_Rounds) +#if defined(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 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 +#if PLATFORM(PPC64) || PLATFORM(X86_64) +// 64-bit emulation provided by the compiler is likely to be slower than dtoa own code on 32-bit hardware. +#define USE_LONG_LONG #endif -#ifdef NO_LONG_LONG -#undef ULLong +#ifndef USE_LONG_LONG #ifdef Just_16 #undef Pack_32 -/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. +/* When Pack_32 is not defined, we store 16 bits per 32-bit int32_t. * 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. + * slower. Hence the default is now to store 32 bits per int32_t. */ #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; +struct Bigint { + struct Bigint* next; + int k, maxwds, sign, wds; + uint32_t x[1]; +}; - static Bigint *freelist[Kmax+1]; - - static Bigint * -Balloc -#ifdef KR_headers - (k) int k; -#else - (int k) -#endif +static Bigint* Balloc(int k) { - 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 + int x = 1 << k; + Bigint* rv = (Bigint*)fastMalloc(sizeof(Bigint) + (x - 1)*sizeof(uint32_t)); + rv->k = k; + rv->maxwds = x; + rv->next = 0; + rv->sign = rv->wds = 0; + + return rv; +} + +static void Bfree(Bigint* v) { - 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 + fastFree(v); +} + +#define Bcopy(x, y) memcpy((char*)&x->sign, (char*)&y->sign, y->wds * sizeof(int32_t) + 2 * sizeof(int)) + +static Bigint* multadd(Bigint* b, int m, int a) /* multiply by m and add a */ { - int i, wds; -#ifdef ULLong - ULong *x; - ULLong carry, y; +#ifdef USE_LONG_LONG + unsigned long long carry; #else - ULong carry, *x, y; -#ifdef Pack_32 - ULong xi, z; + uint32_t carry; #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; + int wds = b->wds; + uint32_t* x = b->x; + int i = 0; + carry = a; + do { +#ifdef USE_LONG_LONG + unsigned long long y = *x * (unsigned long long)m + carry; + carry = y >> 32; + *x++ = (uint32_t)y & 0xffffffffUL; #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 + uint32_t xi = *x; + uint32_t y = (xi & 0xffff) * m + carry; + uint32_t z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + uint32_t y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } while (++i < wds); + + if (carry) { + if (wds >= b->maxwds) { + Bigint* b1 = Balloc(b->k + 1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = (uint32_t)carry; + b->wds = wds; + } + return b; +} + +static Bigint* s2b(const char* s, int nd0, int nd, uint32_t y9) { - Bigint *b; - int i, k; - Long x, y; + int k; + int32_t y; + int32_t x = (nd + 8) / 9; - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; + 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 + Bigint* b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + Bigint* b = Balloc(k + 1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + int 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(uint32_t x) { - 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 + 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 (uint32_t* y) { - 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 + int k; + uint32_t 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(int i) { - 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* b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; +} + +static Bigint* mult(Bigint* a, Bigint* b) { - 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; - } - } + Bigint* c; + int k, wa, wb, wc; + uint32_t *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + uint32_t y; +#ifdef USE_LONG_LONG + unsigned long long carry, z; +#else + uint32_t carry, z; +#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 USE_LONG_LONG + for (; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (unsigned long long)y + *xc + carry; + carry = z >> 32; + *xc++ = (uint32_t)z & 0xffffffffUL; + } while (x < xae); + *xc = (uint32_t)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 + 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; + uint32_t 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; + uint32_t 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 int p5s_count; + +static Bigint* pow5mult(Bigint* b, int k) { - 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 + static int p05[3] = { 5, 25, 125 }; + + if (int i = k & 3) + b = multadd(b, p05[i - 1], 0); + + if (!(k >>= 2)) + return b; + +#if ENABLE(JSC_MULTIPLE_THREADS) + s_dtoaP5Mutex->lock(); +#endif + Bigint* p5 = p5s; + if (!p5) { + /* first time */ + p5 = p5s = i2b(625); + p5s_count = 1; + } + int p5s_count_local = p5s_count; +#if ENABLE(JSC_MULTIPLE_THREADS) + s_dtoaP5Mutex->unlock(); +#endif + int p5s_used = 0; + + for (;;) { + if (k & 1) { + Bigint* b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + + if (++p5s_used == p5s_count_local) { +#if ENABLE(JSC_MULTIPLE_THREADS) + s_dtoaP5Mutex->lock(); +#endif + if (p5s_used == p5s_count) { + ASSERT(!p5->next); + p5->next = mult(p5, p5); + ++p5s_count; + } + + p5s_count_local = p5s_count; +#if ENABLE(JSC_MULTIPLE_THREADS) + s_dtoaP5Mutex->unlock(); +#endif + } + p5 = p5->next; + } + + return b; +} + +static Bigint* lshift(Bigint* b, int k) { - int i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; + Bigint* result = b; #ifdef Pack_32 - n = k >> 5; + int 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) + int n = k >> 4; #endif + + int k1 = b->k; + int n1 = n + b->wds + 1; + for (int i = b->maxwds; n1 > i; i <<= 1) + k1++; + if (b->k < k1) + result = Balloc(k1); + + const uint32_t* srcStart = b->x; + uint32_t* dstStart = result->x; + const uint32_t* src = srcStart + b->wds - 1; + uint32_t* dst = dstStart + n1 - 1; +#ifdef Pack_32 + if (k &= 0x1f) { + uint32_t hiSubword = 0; + int s = 32 - k; + for (; src >= srcStart; --src) { + *dst-- = hiSubword | *src >> s; + hiSubword = *src << k; + } + *dst = hiSubword; + ASSERT(dst == dstStart + n); + result->wds = b->wds + n + (result->x[n1 - 1] != 0); + } +#else + if (k &= 0xf) { + uint32_t hiSubword = 0; + int s = 16 - k; + for (; src >= srcStart; --src) { + *dst-- = hiSubword | *src >> s; + hiSubword = (*src << k) & 0xffff; + } + *dst = hiSubword; + ASSERT(dst == dstStart + n); + result->wds = b->wds + n + (result->x[n1 - 1] != 0); + } + #endif + else { + do { + *--dst = *src--; + } while (src >= srcStart); + result->wds = b->wds + n; + } + for (dst = dstStart + n; dst != dstStart; ) + *--dst = 0; + + if (result != b) + Bfree(b); + return result; +} + +static int cmp(Bigint* a, Bigint* b) { - 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 + uint32_t *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; + ASSERT(i <= 1 || a->x[i - 1]); + ASSERT(j <= 1 || b->x[j - 1]); + 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(Bigint* a, Bigint* b) { - 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 + Bigint* c; + int i, wa, wb; + uint32_t *xa, *xae, *xb, *xbe, *xc; + + 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; +#ifdef USE_LONG_LONG + unsigned long long borrow = 0; + do { + unsigned long long y = (unsigned long long)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (uint32_t)1; + *xc++ = (uint32_t)y & 0xffffffffUL; + } while (xb < xbe); + while (xa < xae) { + unsigned long long y = *xa++ - borrow; + borrow = y >> 32 & (uint32_t)1; + *xc++ = (uint32_t)y & 0xffffffffUL; + } +#else + uint32_t borrow = 0; #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 + do { + uint32_t y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + uint32_t z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } while (xb < xbe); + while (xa < xae) { + uint32_t y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + uint32_t z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + uint32_t y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } while (xb < xbe); + while (xa < xae) { + uint32_t y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while (!*--xc) + wa--; + c->wds = wa; + return c; +} + +static double ulp(double x) { - register Long L; - double a; + register int32_t L; + double a; - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; + L = (word0(x) & Exp_mask) - (P - 1) * Exp_msk1; #ifndef Avoid_Underflow #ifndef Sudden_Underflow - if (L > 0) { -#endif + if (L > 0) { #endif -#ifdef IBM - L |= Exp_msk1 >> 4; #endif - word0(a) = L; - word1(a) = 0; + 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 + } 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(Bigint* a, int* e) { - ULong *xa, *xa0, w, y, z; - int k; - double d; -#ifdef VAX - ULong d0, d1; -#else + uint32_t* xa; + uint32_t* xa0; + uint32_t w; + uint32_t y; + uint32_t z; + int k; + double d; + #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; + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; + ASSERT(y); + 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 + 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: #undef d0 #undef d1 -#endif - return dval(d); - } + 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 +static Bigint* d2b(double d, int* e, int* bits) { - Bigint *b; - int de, k; - ULong *x, y, z; + Bigint* b; + int de, k; + uint32_t *x, y, z; #ifndef Sudden_Underflow - int i; + 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); + b = Balloc(1); #else - b = Balloc(2); + b = Balloc(2); #endif - x = b->x; + x = b->x; - z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + 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 + de = (int)(d0 >> Exp_shift); #else - if ((de = (int)(d0 >> Exp_shift))) - z |= Exp_msk1; + 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; + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << 32 - k; + z >>= k; + } else + x[0] = y; #ifndef Sudden_Underflow - i = + 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; + b->wds = (x[1] = z) ? 2 : 1; + } else { + 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; + 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 { + 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; + if (de) { #endif + *e = de - Bias - (P - 1) + k; + *bits = P - k; #ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; + } else { + *e = de - Bias - (P - 1) + 1 + k; #ifdef Pack_32 - *bits = 32*i - hi0bits(x[i-1]); + *bits = (32 * i) - hi0bits(x[i - 1]); #else - *bits = (i+2)*16 - hi0bits(x[i]); + *bits = (i + 2) * 16 - hi0bits(x[i]); #endif - } + } #endif - return b; - } + return b; +} #undef d0 #undef d1 - static double -ratio -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif +static double ratio(Bigint* a, Bigint* b) { - double da, db; - int k, ka, kb; + double da, db; + int k, ka, kb; - dval(da) = b2d(a, &ka); - dval(db) = b2d(b, &kb); + dval(da) = b2d(a, &ka); + dval(db) = b2d(b, &kb); #ifdef Pack_32 - k = ka - kb + 32*(a->wds - b->wds); + 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, + k = ka - kb + 16 * (a->wds - b->wds); +#endif + if (k > 0) + word0(da) += k * Exp_msk1; + else { + k = -k; + word0(db) += k * Exp_msk1; + } + 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 +}; + +static const double 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 */ + 9007199254740992. * 9007199254740992.e-256 + /* = 2^106 * 1e-53 */ #else - 1e-256 + 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 +#if defined(INFNAN_CHECK) #ifndef NAN_WORD0 #define NAN_WORD0 0x7ff80000 @@ -1457,1110 +1014,853 @@ static CONST_ double tinytens[] = { 1e-16, 1e-32 }; #define NAN_WORD1 0 #endif - static int -match -#ifdef KR_headers - (sp, t) char **sp, *t; -#else - (CONST_ char **sp, CONST_ char *t) -#endif +static int match(const char** sp, const char* t) { - 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; - } + 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 +static void hexnan(double* rvp, const char** sp) { - 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]; - } - } + uint32_t 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 +double strtod(const char* s00, char** se) { #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; + 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; + int32_t L; + uint32_t 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) { + int inexact, oldinexact; +#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; + /* 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); + if (*s == '(') /*)*/ + hexnan(&rv, &s); #endif - goto ret; - } - } + 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) { +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; - } - } + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(rv) = tens[k - 9] * dval(rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG && Flt_Rounds == 1) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { + /* rv = */ rounded_product(dval(rv), tens[e]); + goto ret; + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + dval(rv) *= tens[i]; + /* rv = */ rounded_product(dval(rv), tens[e]); + 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 + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(dval(rv), tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + #ifdef SET_INEXACT - inexact = 1; - if (k <= DBL_DIG) - oldinexact = get_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: + scale = 0; +#endif + + /* 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*/ + errno = ERANGE; +#endif + /* Can't trust HUGE_VAL */ + word0(rv) = Exp_mask; + word1(rv) = 0; #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; + /* set overflow bit */ + dval(rv0) = 1e300; + dval(rv0) *= dval(rv0); +#endif + 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.; + 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; + errno = ERANGE; #endif - if (bd0) - goto retfree; - goto ret; - } + 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 + 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 Avoid_Underflow - j = bbe - scale; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; + 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 + j = P + 1 - bbbits; #else /*Sudden_Underflow*/ - j = bbe; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; + 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: + bb2 += j; + bd2 += j; #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; - } - } + 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); + + 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 Avoid_Underflow - if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) - word0(adj) += (2*P+1)*Exp_msk1 - y; + || (word0(rv) & Exp_mask) <= (2 * P + 1) * Exp_msk1 #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 + || (word0(rv) & Exp_mask) <= Exp_msk1 #endif - ) { + ) { #ifdef SET_INEXACT - if (!delta->x[0] && delta->wds <= 1) - inexact = 0; + if (!delta->x[0] && delta->wds <= 1) + inexact = 0; #endif - break; - } - if (!delta->x[0] && delta->wds <= 1) { - /* exact result */ + 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) == ( + 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; + (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; + word1(rv) = 0; #ifdef Avoid_Underflow - dsign = 0; -#endif - break; - } - } - else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { - drop_down: - /* boundary case -- decrement exponent */ + 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 + L = word0(rv) & Exp_mask; #ifdef Avoid_Underflow - if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) + if (L <= (scale ? (2 * P + 1) * Exp_msk1 : Exp_msk1)) #else - if (L <= Exp_msk1) + if (L <= Exp_msk1) #endif /*Avoid_Underflow*/ -#endif /*IBM*/ - goto undfl; - L -= Exp_msk1; + 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; - } - } + 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; + 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)); + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; + break; + } + if (!(word1(rv) & LSB)) + break; + if (dsign) + dval(rv) += ulp(dval(rv)); + else { + dval(rv) -= ulp(dval(rv)); #ifndef Sudden_Underflow - if (!dval(rv)) - goto undfl; + 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) { + dsign = 1 - dsign; +#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; + 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; + 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 { + } + 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; + if (scale && y <= 2 * P * Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = (uint32_t)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; - } + 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; + if ((word0(rv) & Exp_mask) <= P * Exp_msk1) + { + 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; + /* 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; + } + 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); - } + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (int32_t)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(); + 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); + 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; + /* 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 + 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(Bigint* b, Bigint* S) { - int n; - ULong *bx, *bxe, q, *sx, *sxe; -#ifdef ULLong - ULLong borrow, carry, y, ys; + int n; + uint32_t *bx, *bxe, q, *sx, *sxe; +#ifdef USE_LONG_LONG + unsigned long long borrow, carry, y, ys; #else - ULong borrow, carry, y, ys; + uint32_t 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; + uint32_t si, z, zs; +#endif +#endif + + n = S->wds; + ASSERT_WITH_MESSAGE(b->wds <= n, "oversize b in quorem"); + 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 */ + ASSERT_WITH_MESSAGE(q <= 9, "oversized quotient in quorem"); + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef USE_LONG_LONG + ys = *sx++ * (unsigned long long)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & (uint32_t)1; + *bx++ = (uint32_t)y & 0xffffffffUL; #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; + 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 USE_LONG_LONG + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & (uint32_t)1; + *bx++ = (uint32_t)y & 0xffffffffUL; #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) + 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; +} + +#if !ENABLE(JSC_MULTIPLE_THREADS) +static char* dtoa_result; #endif + +static char* rv_alloc(int i) { - char *rv, *t; + int k; + + int j = sizeof(uint32_t); + for (k = 0; + sizeof(Bigint) - sizeof(uint32_t) - sizeof(int) + j <= (unsigned)i; + j <<= 1) + k++; + int* r = (int*)Balloc(k); + *r = k; + return +#if !ENABLE(JSC_MULTIPLE_THREADS) + dtoa_result = +#endif + (char*)(r + 1); +} - t = rv = rv_alloc(n); - while((*t = *s++)) t++; - if (rve) - *rve = t; - return rv; - } +static char* nrv_alloc(const char* s, char** rve, int n) +{ + char* rv = rv_alloc(n); + char* t = rv; + + 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, @@ -2568,21 +1868,16 @@ nrv_alloc(CONST_ char *s, char **rve, int n) * when MULTIPLE_THREADS is not defined. */ - void -#ifdef KR_headers -freedtoa(s) char *s; -#else -freedtoa(char *s) -#endif +void freedtoa(char* s) { - 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; + Bigint* b = (Bigint*)((int*)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#if !ENABLE(JSC_MULTIPLE_THREADS) + if (s == dtoa_result) + dtoa_result = 0; #endif - } +} /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. * @@ -2590,714 +1885,554 @@ freedtoa(char *s) * 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. + * 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 int32_t + * 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 +char* dtoa(double d, int ndigits, int* decpt, int* sign, char** rve) { - /* 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; + /* + 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. + + */ + + 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; + int32_t 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; + int denorm; + uint32_t x; #endif + Bigint *b, *b1, *delta, *mlo = NULL, *mhi, *S; + double d2, ds, eps; + char *s, *s0; #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); - } + int inexact, oldinexact; +#endif + +#if !ENABLE(JSC_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 ((word0(d) & Exp_mask) == Exp_mask) + { + /* Infinity or NaN */ + *decpt = 9999; + if (!word1(d) && !(word0(d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); + return nrv_alloc("NaN", rve, 3); + } + 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; - } + try_quick = oldinexact = get_inexact(); + inexact = 1; #endif - b = d2b(dval(d), &be, &bbits); + 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 + 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; + + /* 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; #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; + 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; + } #ifndef SET_INEXACT #ifdef Check_FLT_ROUNDS - try_quick = Rounding == 1; + try_quick = Rounding == 1; #else - try_quick = 1; + 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; - } + leftright = 1; + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + s = s0 = rv_alloc(i); + + 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; - } - } + 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 = (int32_t)(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; + } +#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 = (int32_t)(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)) { + /* 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 = + inexact = 0; +#endif + break; + } + if (i == ilim) { + 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) + denorm ? be + (Bias + (P - 1) - 1 + 1) : +#endif + 1 + P - bbits; + 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 (!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. - */ + && 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++; + 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 (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); + if (j1 == 0 && !(word1(d) & 1)) { + 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) { + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } + if (j < 0 || j == 0 && !(word1(d) & 1)) { + 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) { + inexact = 0; +#endif + goto accept_dig; + } + 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) { + if (dig == '9') { /* possible if i == 1 */ +round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *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: + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + 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 { + while (*--s == '0') { } + s++; + } + goto ret; +no_digits: + k = -1 - ndigits; + goto ret; +one_digit: + *s++ = '1'; + k++; + goto ret; +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 + 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; } -#endif + +} // namespace JSC diff --git a/JavaScriptCore/kjs/dtoa.h b/JavaScriptCore/kjs/dtoa.h index 79ff828..690ebc8 100644 --- a/JavaScriptCore/kjs/dtoa.h +++ b/JavaScriptCore/kjs/dtoa.h @@ -1,7 +1,5 @@ -// -*- c-basic-offset: 2 -*- /* - * This file is part of the KDE libraries - * Copyright (C) 2003 Apple Computer, Inc. + * 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 Library General Public @@ -20,12 +18,21 @@ * */ -#ifndef _KJS_DTOA_H_ -#define _KJS_DTOA_H_ +#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); +namespace WTF { + class Mutex; +} -#endif /* _KJS_DTOA_H */ +namespace JSC { + + extern WTF::Mutex* s_dtoaP5Mutex; + + double strtod(const char* s00, char** se); + char* dtoa(double d, int ndigits, int* decpt, int* sign, char** rve); + void freedtoa(char* s); + +} // namespace JSC + +#endif /* KJS_dtoa_h */ diff --git a/JavaScriptCore/kjs/error_object.cpp b/JavaScriptCore/kjs/error_object.cpp deleted file mode 100644 index 75ef0ba..0000000 --- a/JavaScriptCore/kjs/error_object.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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 deleted file mode 100644 index 9734085..0000000 --- a/JavaScriptCore/kjs/error_object.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 deleted file mode 100644 index 4ba54fd..0000000 --- a/JavaScriptCore/kjs/function.cpp +++ /dev/null @@ -1,885 +0,0 @@ -// -*- 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, int attr) -{ - if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) - return; - InternalFunctionImp::put(exec, propertyName, value, attr); -} - -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, int attr) -{ - if (indexToNameMap.isMapped(propertyName)) { - _activationObject->put(exec, indexToNameMap[propertyName], value, attr); - } else { - JSObject::put(exec, propertyName, value, attr); - } -} - -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, int attr) -{ - // If any bits other than DontDelete are set, then we bypass the read-only check. - bool checkReadOnly = !(attr & ~DontDelete); - if (symbolTablePut(propertyName, value, checkReadOnly)) - 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, attr, checkReadOnly); -} - -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 deleted file mode 100644 index a45221e..0000000 --- a/JavaScriptCore/kjs/function.h +++ /dev/null @@ -1,161 +0,0 @@ -// -*- 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* value, int attr = None); - 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* value, int attr = None); - 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 deleted file mode 100644 index 5af2970..0000000 --- a/JavaScriptCore/kjs/function_object.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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 - Debugger* dbg = exec->dynamicGlobalObject()->debugger(); - if (dbg) { - // send empty sourceURL to indicate constructed code - bool cont = dbg->sourceParsed(exec, sourceId, UString(), body, lineNumber, errLine, errMsg); - if (!cont) { - dbg->imp()->abort(); - 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 | DontDelete | ReadOnly); - fimp->putDirect(exec->propertyNames().prototype, prototype, Internal | 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 deleted file mode 100644 index cd0fe3e..0000000 --- a/JavaScriptCore/kjs/function_object.h +++ /dev/null @@ -1,61 +0,0 @@ -// -*- 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 index 658ae89..370798d 100644 --- a/JavaScriptCore/kjs/grammar.y +++ b/JavaScriptCore/kjs/grammar.y @@ -1,8 +1,10 @@ +%pure_parser + %{ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -25,20 +27,17 @@ #include <string.h> #include <stdlib.h> -#include "value.h" -#include "object.h" -#include "types.h" +#include "JSValue.h" +#include "JSObject.h" #include "nodes.h" #include "lexer.h" -#include "internal.h" +#include "JSString.h" +#include "JSGlobalData.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 @@ -50,30 +49,38 @@ #define YYERROR_VERBOSE #endif -extern int kjsyylex(); -int kjsyyerror(const char *); -static bool allowAutomaticSemicolon(); +int kjsyylex(void* lvalp, void* llocp, void* globalPtr); +int kjsyyerror(const char*); +static inline bool allowAutomaticSemicolon(JSC::Lexer&, int); + +#define GLOBAL_DATA static_cast<JSGlobalData*>(globalPtr) +#define LEXER (GLOBAL_DATA->lexer) -#define AUTO_SEMICOLON do { if (!allowAutomaticSemicolon()) YYABORT; } while (0) +#define AUTO_SEMICOLON do { if (!allowAutomaticSemicolon(*LEXER, yychar)) YYABORT; } while (0) +#define SET_EXCEPTION_LOCATION(node, start, divot, end) node->setExceptionSourceCode((divot), (divot) - (start), (end) - (divot)) #define DBG(l, s, e) (l)->setLoc((s).first_line, (e).last_line) -using namespace KJS; +using namespace JSC; 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); - +static ExpressionNode* makeAssignNode(void*, ExpressionNode* loc, Operator, ExpressionNode* expr, bool locHasAssignments, bool exprHasAssignments, int start, int divot, int end); +static ExpressionNode* makePrefixNode(void*, ExpressionNode* expr, Operator, int start, int divot, int end); +static ExpressionNode* makePostfixNode(void*, ExpressionNode* expr, Operator, int start, int divot, int end); +static PropertyNode* makeGetterOrSetterPropertyNode(void*, const Identifier &getOrSet, const Identifier& name, ParameterNode*, FunctionBodyNode*, const SourceCode&); +static ExpressionNodeInfo makeFunctionCallNode(void*, ExpressionNodeInfo func, ArgumentsNodeInfo, int start, int divot, int end); +static ExpressionNode* makeTypeOfNode(void*, ExpressionNode*); +static ExpressionNode* makeDeleteNode(void*, ExpressionNode*, int start, int divot, int end); +static ExpressionNode* makeNegateNode(void*, ExpressionNode*); +static NumberNode* makeNumberNode(void*, double); +static ExpressionNode* makeBitwiseNotNode(void*, ExpressionNode*); +static ExpressionNode* makeMultNode(void*, ExpressionNode*, ExpressionNode*, bool rightHasAssignments); +static ExpressionNode* makeDivNode(void*, ExpressionNode*, ExpressionNode*, bool rightHasAssignments); +static ExpressionNode* makeAddNode(void*, ExpressionNode*, ExpressionNode*, bool rightHasAssignments); +static ExpressionNode* makeSubNode(void*, ExpressionNode*, ExpressionNode*, bool rightHasAssignments); +static ExpressionNode* makeLeftShiftNode(void*, ExpressionNode*, ExpressionNode*, bool rightHasAssignments); +static ExpressionNode* makeRightShiftNode(void*, ExpressionNode*, ExpressionNode*, bool rightHasAssignments); +static StatementNode* makeVarStatementNode(void*, ExpressionNode*); +static ExpressionNode* combineVarInitializers(void*, ExpressionNode* list, AssignResolveNode* init); #if COMPILER(MSVC) @@ -89,10 +96,23 @@ static ExpressionNode* combineVarInitializers(ExpressionNode* list, AssignResolv #endif -template <typename T> NodeInfo<T> createNodeInfo(T node, ParserRefCountedData<DeclarationStacks::VarStack>* varDecls, - ParserRefCountedData<DeclarationStacks::FunctionStack>* funcDecls) +#define YYPARSE_PARAM globalPtr +#define YYLEX_PARAM globalPtr + +template <typename T> NodeDeclarationInfo<T> createNodeDeclarationInfo(T node, ParserRefCountedData<DeclarationStacks::VarStack>* varDecls, + ParserRefCountedData<DeclarationStacks::FunctionStack>* funcDecls, + CodeFeatures info, + int numConstants) { - NodeInfo<T> result = {node, varDecls, funcDecls}; + ASSERT((info & ~AllFeatures) == 0); + NodeDeclarationInfo<T> result = {node, varDecls, funcDecls, info, numConstants}; + return result; +} + +template <typename T> NodeInfo<T> createNodeInfo(T node, CodeFeatures info, int numConstants) +{ + ASSERT((info & ~AllFeatures) == 0); + NodeInfo<T> result = {node, info, numConstants}; return result; } @@ -115,21 +135,21 @@ template <typename T> T mergeDeclarationLists(T decls1, T decls2) return decls1; } -static void appendToVarDeclarationList(ParserRefCountedData<DeclarationStacks::VarStack>*& varDecls, const Identifier& ident, unsigned attrs) +static void appendToVarDeclarationList(void* globalPtr, ParserRefCountedData<DeclarationStacks::VarStack>*& varDecls, const Identifier& ident, unsigned attrs) { if (!varDecls) - varDecls = new ParserRefCountedData<DeclarationStacks::VarStack>; + varDecls = new ParserRefCountedData<DeclarationStacks::VarStack>(GLOBAL_DATA); varDecls->data.append(make_pair(ident, attrs)); } -static inline void appendToVarDeclarationList(ParserRefCountedData<DeclarationStacks::VarStack>*& varDecls, ConstDeclNode* decl) +static inline void appendToVarDeclarationList(void* globalPtr, ParserRefCountedData<DeclarationStacks::VarStack>*& varDecls, ConstDeclNode* decl) { unsigned attrs = DeclarationStacks::IsConstant; if (decl->m_init) attrs |= DeclarationStacks::HasInitializer; - appendToVarDeclarationList(varDecls, decl->m_ident, attrs); + appendToVarDeclarationList(globalPtr, varDecls, decl->m_ident, attrs); } %} @@ -137,18 +157,17 @@ static inline void appendToVarDeclarationList(ParserRefCountedData<DeclarationSt %union { int intValue; double doubleValue; - UString* string; Identifier* ident; // expression subtrees - ExpressionNode* expressionNode; - FuncDeclNode* funcDeclNode; - PropertyNode* propertyNode; - ArgumentsNode* argumentsNode; - ConstDeclNode* constDeclNode; + ExpressionNodeInfo expressionNode; + FuncDeclNodeInfo funcDeclNode; + PropertyNodeInfo propertyNode; + ArgumentsNodeInfo argumentsNode; + ConstDeclNodeInfo constDeclNode; CaseBlockNodeInfo caseBlockNode; CaseClauseNodeInfo caseClauseNode; - FuncExprNode* funcExprNode; + FuncExprNodeInfo funcExprNode; // statement nodes StatementNodeInfo statementNode; @@ -156,13 +175,13 @@ static inline void appendToVarDeclarationList(ParserRefCountedData<DeclarationSt ProgramNode* programNode; SourceElementsInfo sourceElements; - PropertyList propertyList; - ArgumentList argumentList; + PropertyListInfo propertyList; + ArgumentListInfo argumentList; VarDeclListInfo varDeclList; ConstDeclListInfo constDeclList; ClauseListInfo clauseList; - ElementList elementList; - ParameterList parameterList; + ElementListInfo elementList; + ParameterListInfo parameterList; Operator op; } @@ -198,11 +217,12 @@ static inline void appendToVarDeclarationList(ParserRefCountedData<DeclarationSt %token RSHIFTEQUAL URSHIFTEQUAL /* >>= and >>>= */ %token ANDEQUAL MODEQUAL /* &= and %= */ %token XOREQUAL OREQUAL /* ^= and |= */ +%token <intValue> OPENBRACE /* { (with char offset) */ +%token <intValue> CLOSEBRACE /* { (with char offset) */ /* terminal types */ %token <doubleValue> NUMBER -%token <string> STRING -%token <ident> IDENT +%token <ident> IDENT STRING /* automatically inserted semicolon */ %token AUTOPLUSPLUS AUTOMINUSMINUS @@ -264,69 +284,90 @@ static inline void appendToVarDeclarationList(ParserRefCountedData<DeclarationSt %% Literal: - NULLTOKEN { $$ = new NullNode; } - | TRUETOKEN { $$ = new TrueNode; } - | FALSETOKEN { $$ = new FalseNode; } - | NUMBER { $$ = makeNumberNode($1); } - | STRING { $$ = new StringNode($1); } + NULLTOKEN { $$ = createNodeInfo<ExpressionNode*>(new NullNode(GLOBAL_DATA), 0, 1); } + | TRUETOKEN { $$ = createNodeInfo<ExpressionNode*>(new BooleanNode(GLOBAL_DATA, true), 0, 1); } + | FALSETOKEN { $$ = createNodeInfo<ExpressionNode*>(new BooleanNode(GLOBAL_DATA, false), 0, 1); } + | NUMBER { $$ = createNodeInfo<ExpressionNode*>(makeNumberNode(GLOBAL_DATA, $1), 0, 1); } + | STRING { $$ = createNodeInfo<ExpressionNode*>(new StringNode(GLOBAL_DATA, *$1), 0, 1); } | '/' /* regexp */ { - Lexer& l = lexer(); + Lexer& l = *LEXER; if (!l.scanRegExp()) YYABORT; - $$ = new RegExpNode(l.pattern(), l.flags()); + RegExpNode* node = new RegExpNode(GLOBAL_DATA, l.pattern(), l.flags()); + int size = l.pattern().size() + 2; // + 2 for the two /'s + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.first_column + size, @1.first_column + size); + $$ = createNodeInfo<ExpressionNode*>(node, 0, 0); } | DIVEQUAL /* regexp with /= */ { - Lexer& l = lexer(); + Lexer& l = *LEXER; if (!l.scanRegExp()) YYABORT; - $$ = new RegExpNode("=" + l.pattern(), l.flags()); + RegExpNode* node = new RegExpNode(GLOBAL_DATA, "=" + l.pattern(), l.flags()); + int size = l.pattern().size() + 2; // + 2 for the two /'s + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.first_column + size, @1.first_column + size); + $$ = createNodeInfo<ExpressionNode*>(node, 0, 0); } ; 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; } + IDENT ':' AssignmentExpr { $$ = createNodeInfo<PropertyNode*>(new PropertyNode(GLOBAL_DATA, *$1, $3.m_node, PropertyNode::Constant), $3.m_features, $3.m_numConstants); } + | STRING ':' AssignmentExpr { $$ = createNodeInfo<PropertyNode*>(new PropertyNode(GLOBAL_DATA, *$1, $3.m_node, PropertyNode::Constant), $3.m_features, $3.m_numConstants); } + | NUMBER ':' AssignmentExpr { $$ = createNodeInfo<PropertyNode*>(new PropertyNode(GLOBAL_DATA, Identifier(GLOBAL_DATA, UString::from($1)), $3.m_node, PropertyNode::Constant), $3.m_features, $3.m_numConstants); } + | IDENT IDENT '(' ')' OPENBRACE FunctionBody CLOSEBRACE { $$ = createNodeInfo<PropertyNode*>(makeGetterOrSetterPropertyNode(globalPtr, *$1, *$2, 0, $6, LEXER->sourceCode($5, $7, @5.first_line)), ClosureFeature, 0); DBG($6, @5, @7); if (!$$.m_node) YYABORT; } + | IDENT IDENT '(' FormalParameterList ')' OPENBRACE FunctionBody CLOSEBRACE + { + $$ = createNodeInfo<PropertyNode*>(makeGetterOrSetterPropertyNode(globalPtr, *$1, *$2, $4.m_node.head, $7, LEXER->sourceCode($6, $8, @6.first_line)), $4.m_features | ClosureFeature, 0); + if ($4.m_features & ArgumentsFeature) + $7->setUsesArguments(); + DBG($7, @6, @8); + if (!$$.m_node) + YYABORT; + } ; PropertyList: - Property { $$.head = new PropertyListNode($1); - $$.tail = $$.head; } - | PropertyList ',' Property { $$.head = $1.head; - $$.tail = new PropertyListNode($3, $1.tail); } + Property { $$.m_node.head = new PropertyListNode(GLOBAL_DATA, $1.m_node); + $$.m_node.tail = $$.m_node.head; + $$.m_features = $1.m_features; + $$.m_numConstants = $1.m_numConstants; } + | PropertyList ',' Property { $$.m_node.head = $1.m_node.head; + $$.m_node.tail = new PropertyListNode(GLOBAL_DATA, $3.m_node, $1.m_node.tail); + $$.m_features = $1.m_features | $3.m_features; + $$.m_numConstants = $1.m_numConstants + $3.m_numConstants; } ; PrimaryExpr: PrimaryExprNoBrace - | '{' '}' { $$ = new ObjectLiteralNode(); } - | '{' PropertyList '}' { $$ = new ObjectLiteralNode($2.head); } + | OPENBRACE CLOSEBRACE { $$ = createNodeInfo<ExpressionNode*>(new ObjectLiteralNode(GLOBAL_DATA), 0, 0); } + | OPENBRACE PropertyList CLOSEBRACE { $$ = createNodeInfo<ExpressionNode*>(new ObjectLiteralNode(GLOBAL_DATA, $2.m_node.head), $2.m_features, $2.m_numConstants); } /* allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 */ - | '{' PropertyList ',' '}' { $$ = new ObjectLiteralNode($2.head); } + | OPENBRACE PropertyList ',' CLOSEBRACE { $$ = createNodeInfo<ExpressionNode*>(new ObjectLiteralNode(GLOBAL_DATA, $2.m_node.head), $2.m_features, $2.m_numConstants); } ; PrimaryExprNoBrace: - THISTOKEN { $$ = new ThisNode(); } + THISTOKEN { $$ = createNodeInfo<ExpressionNode*>(new ThisNode(GLOBAL_DATA), ThisFeature, 0); } | Literal | ArrayLiteral - | IDENT { $$ = new ResolveNode(*$1); } + | IDENT { $$ = createNodeInfo<ExpressionNode*>(new ResolveNode(GLOBAL_DATA, *$1, @1.first_column), (*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0, 0); } | '(' Expr ')' { $$ = $2; } ; ArrayLiteral: - '[' ElisionOpt ']' { $$ = new ArrayNode($2); } - | '[' ElementList ']' { $$ = new ArrayNode($2.head); } - | '[' ElementList ',' ElisionOpt ']' { $$ = new ArrayNode($4, $2.head); } + '[' ElisionOpt ']' { $$ = createNodeInfo<ExpressionNode*>(new ArrayNode(GLOBAL_DATA, $2), 0, $2 ? 1 : 0); } + | '[' ElementList ']' { $$ = createNodeInfo<ExpressionNode*>(new ArrayNode(GLOBAL_DATA, $2.m_node.head), $2.m_features, $2.m_numConstants); } + | '[' ElementList ',' ElisionOpt ']' { $$ = createNodeInfo<ExpressionNode*>(new ArrayNode(GLOBAL_DATA, $4, $2.m_node.head), $2.m_features, $4 ? $2.m_numConstants + 1 : $2.m_numConstants); } ; ElementList: - ElisionOpt AssignmentExpr { $$.head = new ElementNode($1, $2); - $$.tail = $$.head; } + ElisionOpt AssignmentExpr { $$.m_node.head = new ElementNode(GLOBAL_DATA, $1, $2.m_node); + $$.m_node.tail = $$.m_node.head; + $$.m_features = $2.m_features; + $$.m_numConstants = $2.m_numConstants; } | ElementList ',' ElisionOpt AssignmentExpr - { $$.head = $1.head; - $$.tail = new ElementNode($1.tail, $3, $4); } + { $$.m_node.head = $1.m_node.head; + $$.m_node.tail = new ElementNode(GLOBAL_DATA, $1.m_node.tail, $3, $4.m_node); + $$.m_features = $1.m_features | $4.m_features; + $$.m_numConstants = $1.m_numConstants + $4.m_numConstants; } ; ElisionOpt: @@ -341,53 +382,92 @@ Elision: MemberExpr: PrimaryExpr - | FunctionExpr { $$ = $1; } - | MemberExpr '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } - | MemberExpr '.' IDENT { $$ = new DotAccessorNode($1, *$3); } - | NEW MemberExpr Arguments { $$ = new NewExprNode($2, $3); } + | FunctionExpr { $$ = createNodeInfo<ExpressionNode*>($1.m_node, $1.m_features, $1.m_numConstants); } + | MemberExpr '[' Expr ']' { BracketAccessorNode* node = new BracketAccessorNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @4.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); + } + | MemberExpr '.' IDENT { DotAccessorNode* node = new DotAccessorNode(GLOBAL_DATA, $1.m_node, *$3); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features, $1.m_numConstants); + } + | NEW MemberExpr Arguments { NewExprNode* node = new NewExprNode(GLOBAL_DATA, $2.m_node, $3.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $2.m_features | $3.m_features, $2.m_numConstants + $3.m_numConstants); + } ; MemberExprNoBF: PrimaryExprNoBrace - | MemberExprNoBF '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } - | MemberExprNoBF '.' IDENT { $$ = new DotAccessorNode($1, *$3); } - | NEW MemberExpr Arguments { $$ = new NewExprNode($2, $3); } + | MemberExprNoBF '[' Expr ']' { BracketAccessorNode* node = new BracketAccessorNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @4.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); + } + | MemberExprNoBF '.' IDENT { DotAccessorNode* node = new DotAccessorNode(GLOBAL_DATA, $1.m_node, *$3); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features, $1.m_numConstants); + } + | NEW MemberExpr Arguments { NewExprNode* node = new NewExprNode(GLOBAL_DATA, $2.m_node, $3.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $2.m_features | $3.m_features, $2.m_numConstants + $3.m_numConstants); + } ; NewExpr: MemberExpr - | NEW NewExpr { $$ = new NewExprNode($2); } + | NEW NewExpr { NewExprNode* node = new NewExprNode(GLOBAL_DATA, $2.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $2.m_features, $2.m_numConstants); + } ; NewExprNoBF: MemberExprNoBF - | NEW NewExpr { $$ = new NewExprNode($2); } + | NEW NewExpr { NewExprNode* node = new NewExprNode(GLOBAL_DATA, $2.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $2.m_features, $2.m_numConstants); + } ; CallExpr: - MemberExpr Arguments { $$ = makeFunctionCallNode($1, $2); } - | CallExpr Arguments { $$ = makeFunctionCallNode($1, $2); } - | CallExpr '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } - | CallExpr '.' IDENT { $$ = new DotAccessorNode($1, *$3); } + MemberExpr Arguments { $$ = makeFunctionCallNode(globalPtr, $1, $2, @1.first_column, @1.last_column, @2.last_column); } + | CallExpr Arguments { $$ = makeFunctionCallNode(globalPtr, $1, $2, @1.first_column, @1.last_column, @2.last_column); } + | CallExpr '[' Expr ']' { BracketAccessorNode* node = new BracketAccessorNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @4.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); + } + | CallExpr '.' IDENT { DotAccessorNode* node = new DotAccessorNode(GLOBAL_DATA, $1.m_node, *$3); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features, $1.m_numConstants); } ; CallExprNoBF: - MemberExprNoBF Arguments { $$ = makeFunctionCallNode($1, $2); } - | CallExprNoBF Arguments { $$ = makeFunctionCallNode($1, $2); } - | CallExprNoBF '[' Expr ']' { $$ = new BracketAccessorNode($1, $3); } - | CallExprNoBF '.' IDENT { $$ = new DotAccessorNode($1, *$3); } + MemberExprNoBF Arguments { $$ = makeFunctionCallNode(globalPtr, $1, $2, @1.first_column, @1.last_column, @2.last_column); } + | CallExprNoBF Arguments { $$ = makeFunctionCallNode(globalPtr, $1, $2, @1.first_column, @1.last_column, @2.last_column); } + | CallExprNoBF '[' Expr ']' { BracketAccessorNode* node = new BracketAccessorNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @4.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); + } + | CallExprNoBF '.' IDENT { DotAccessorNode* node = new DotAccessorNode(GLOBAL_DATA, $1.m_node, *$3); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features, $1.m_numConstants); + } ; Arguments: - '(' ')' { $$ = new ArgumentsNode(); } - | '(' ArgumentList ')' { $$ = new ArgumentsNode($2.head); } + '(' ')' { $$ = createNodeInfo<ArgumentsNode*>(new ArgumentsNode(GLOBAL_DATA), 0, 0); } + | '(' ArgumentList ')' { $$ = createNodeInfo<ArgumentsNode*>(new ArgumentsNode(GLOBAL_DATA, $2.m_node.head), $2.m_features, $2.m_numConstants); } ; ArgumentList: - AssignmentExpr { $$.head = new ArgumentListNode($1); - $$.tail = $$.head; } - | ArgumentList ',' AssignmentExpr { $$.head = $1.head; - $$.tail = new ArgumentListNode($1.tail, $3); } + AssignmentExpr { $$.m_node.head = new ArgumentListNode(GLOBAL_DATA, $1.m_node); + $$.m_node.tail = $$.m_node.head; + $$.m_features = $1.m_features; + $$.m_numConstants = $1.m_numConstants; } + | ArgumentList ',' AssignmentExpr { $$.m_node.head = $1.m_node.head; + $$.m_node.tail = new ArgumentListNode(GLOBAL_DATA, $1.m_node.tail, $3.m_node); + $$.m_features = $1.m_features | $3.m_features; + $$.m_numConstants = $1.m_numConstants + $3.m_numConstants; } ; LeftHandSideExpr: @@ -402,28 +482,28 @@ LeftHandSideExprNoBF: PostfixExpr: LeftHandSideExpr - | LeftHandSideExpr PLUSPLUS { $$ = makePostfixNode($1, OpPlusPlus); } - | LeftHandSideExpr MINUSMINUS { $$ = makePostfixNode($1, OpMinusMinus); } + | LeftHandSideExpr PLUSPLUS { $$ = createNodeInfo<ExpressionNode*>(makePostfixNode(GLOBAL_DATA, $1.m_node, OpPlusPlus, @1.first_column, @1.last_column, @2.last_column), $1.m_features | AssignFeature, $1.m_numConstants); } + | LeftHandSideExpr MINUSMINUS { $$ = createNodeInfo<ExpressionNode*>(makePostfixNode(GLOBAL_DATA, $1.m_node, OpMinusMinus, @1.first_column, @1.last_column, @2.last_column), $1.m_features | AssignFeature, $1.m_numConstants); } ; PostfixExprNoBF: LeftHandSideExprNoBF - | LeftHandSideExprNoBF PLUSPLUS { $$ = makePostfixNode($1, OpPlusPlus); } - | LeftHandSideExprNoBF MINUSMINUS { $$ = makePostfixNode($1, OpMinusMinus); } + | LeftHandSideExprNoBF PLUSPLUS { $$ = createNodeInfo<ExpressionNode*>(makePostfixNode(GLOBAL_DATA, $1.m_node, OpPlusPlus, @1.first_column, @1.last_column, @2.last_column), $1.m_features | AssignFeature, $1.m_numConstants); } + | LeftHandSideExprNoBF MINUSMINUS { $$ = createNodeInfo<ExpressionNode*>(makePostfixNode(GLOBAL_DATA, $1.m_node, OpMinusMinus, @1.first_column, @1.last_column, @2.last_column), $1.m_features | AssignFeature, $1.m_numConstants); } ; 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); } + DELETETOKEN UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makeDeleteNode(GLOBAL_DATA, $2.m_node, @1.first_column, @2.last_column, @2.last_column), $2.m_features, $2.m_numConstants); } + | VOIDTOKEN UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(new VoidNode(GLOBAL_DATA, $2.m_node), $2.m_features, $2.m_numConstants + 1); } + | TYPEOF UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makeTypeOfNode(GLOBAL_DATA, $2.m_node), $2.m_features, $2.m_numConstants); } + | PLUSPLUS UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makePrefixNode(GLOBAL_DATA, $2.m_node, OpPlusPlus, @1.first_column, @2.first_column + 1, @2.last_column), $2.m_features | AssignFeature, $2.m_numConstants); } + | AUTOPLUSPLUS UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makePrefixNode(GLOBAL_DATA, $2.m_node, OpPlusPlus, @1.first_column, @2.first_column + 1, @2.last_column), $2.m_features | AssignFeature, $2.m_numConstants); } + | MINUSMINUS UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makePrefixNode(GLOBAL_DATA, $2.m_node, OpMinusMinus, @1.first_column, @2.first_column + 1, @2.last_column), $2.m_features | AssignFeature, $2.m_numConstants); } + | AUTOMINUSMINUS UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makePrefixNode(GLOBAL_DATA, $2.m_node, OpMinusMinus, @1.first_column, @2.first_column + 1, @2.last_column), $2.m_features | AssignFeature, $2.m_numConstants); } + | '+' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(new UnaryPlusNode(GLOBAL_DATA, $2.m_node), $2.m_features, $2.m_numConstants); } + | '-' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makeNegateNode(GLOBAL_DATA, $2.m_node), $2.m_features, $2.m_numConstants); } + | '~' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makeBitwiseNotNode(GLOBAL_DATA, $2.m_node), $2.m_features, $2.m_numConstants); } + | '!' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(new LogicalNotNode(GLOBAL_DATA, $2.m_node), $2.m_features, $2.m_numConstants); } UnaryExpr: PostfixExpr @@ -437,228 +517,245 @@ UnaryExprNoBF: MultiplicativeExpr: UnaryExpr - | MultiplicativeExpr '*' UnaryExpr { $$ = new MultNode($1, $3); } - | MultiplicativeExpr '/' UnaryExpr { $$ = new DivNode($1, $3); } - | MultiplicativeExpr '%' UnaryExpr { $$ = new ModNode($1, $3); } + | MultiplicativeExpr '*' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makeMultNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | MultiplicativeExpr '/' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(makeDivNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | MultiplicativeExpr '%' UnaryExpr { $$ = createNodeInfo<ExpressionNode*>(new ModNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; MultiplicativeExprNoBF: UnaryExprNoBF | MultiplicativeExprNoBF '*' UnaryExpr - { $$ = new MultNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeMultNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | MultiplicativeExprNoBF '/' UnaryExpr - { $$ = new DivNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeDivNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | MultiplicativeExprNoBF '%' UnaryExpr - { $$ = new ModNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new ModNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; AdditiveExpr: MultiplicativeExpr - | AdditiveExpr '+' MultiplicativeExpr { $$ = makeAddNode($1, $3); } - | AdditiveExpr '-' MultiplicativeExpr { $$ = new SubNode($1, $3); } + | AdditiveExpr '+' MultiplicativeExpr { $$ = createNodeInfo<ExpressionNode*>(makeAddNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | AdditiveExpr '-' MultiplicativeExpr { $$ = createNodeInfo<ExpressionNode*>(makeSubNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; AdditiveExprNoBF: MultiplicativeExprNoBF | AdditiveExprNoBF '+' MultiplicativeExpr - { $$ = makeAddNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeAddNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | AdditiveExprNoBF '-' MultiplicativeExpr - { $$ = new SubNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeSubNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; ShiftExpr: AdditiveExpr - | ShiftExpr LSHIFT AdditiveExpr { $$ = new LeftShiftNode($1, $3); } - | ShiftExpr RSHIFT AdditiveExpr { $$ = new RightShiftNode($1, $3); } - | ShiftExpr URSHIFT AdditiveExpr { $$ = new UnsignedRightShiftNode($1, $3); } + | ShiftExpr LSHIFT AdditiveExpr { $$ = createNodeInfo<ExpressionNode*>(makeLeftShiftNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | ShiftExpr RSHIFT AdditiveExpr { $$ = createNodeInfo<ExpressionNode*>(makeRightShiftNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | ShiftExpr URSHIFT AdditiveExpr { $$ = createNodeInfo<ExpressionNode*>(new UnsignedRightShiftNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; ShiftExprNoBF: AdditiveExprNoBF - | ShiftExprNoBF LSHIFT AdditiveExpr { $$ = new LeftShiftNode($1, $3); } - | ShiftExprNoBF RSHIFT AdditiveExpr { $$ = new RightShiftNode($1, $3); } - | ShiftExprNoBF URSHIFT AdditiveExpr { $$ = new UnsignedRightShiftNode($1, $3); } + | ShiftExprNoBF LSHIFT AdditiveExpr { $$ = createNodeInfo<ExpressionNode*>(makeLeftShiftNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | ShiftExprNoBF RSHIFT AdditiveExpr { $$ = createNodeInfo<ExpressionNode*>(makeRightShiftNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | ShiftExprNoBF URSHIFT AdditiveExpr { $$ = createNodeInfo<ExpressionNode*>(new UnsignedRightShiftNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; 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); } + | RelationalExpr '<' ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new LessNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExpr '>' ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new GreaterNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExpr LE ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new LessEqNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExpr GE ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new GreaterEqNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExpr INSTANCEOF ShiftExpr { InstanceOfNode* node = new InstanceOfNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @3.first_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExpr INTOKEN ShiftExpr { InNode* node = new InNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @3.first_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; 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 '<' ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new LessNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoIn '>' ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new GreaterNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoIn LE ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new LessEqNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoIn GE ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new GreaterEqNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | RelationalExprNoIn INSTANCEOF ShiftExpr - { $$ = new InstanceOfNode($1, $3); } + { InstanceOfNode* node = new InstanceOfNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @3.first_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; 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 '<' ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new LessNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoBF '>' ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new GreaterNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoBF LE ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new LessEqNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoBF GE ShiftExpr { $$ = createNodeInfo<ExpressionNode*>(new GreaterEqNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | RelationalExprNoBF INSTANCEOF ShiftExpr - { $$ = new InstanceOfNode($1, $3); } - | RelationalExprNoBF INTOKEN ShiftExpr { $$ = new InNode($1, $3); } + { InstanceOfNode* node = new InstanceOfNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @3.first_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | RelationalExprNoBF INTOKEN ShiftExpr + { InNode* node = new InNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @3.first_column, @3.last_column); + $$ = createNodeInfo<ExpressionNode*>(node, $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; 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); } + | EqualityExpr EQEQ RelationalExpr { $$ = createNodeInfo<ExpressionNode*>(new EqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | EqualityExpr NE RelationalExpr { $$ = createNodeInfo<ExpressionNode*>(new NotEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | EqualityExpr STREQ RelationalExpr { $$ = createNodeInfo<ExpressionNode*>(new StrictEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | EqualityExpr STRNEQ RelationalExpr { $$ = createNodeInfo<ExpressionNode*>(new NotStrictEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; EqualityExprNoIn: RelationalExprNoIn | EqualityExprNoIn EQEQ RelationalExprNoIn - { $$ = new EqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new EqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | EqualityExprNoIn NE RelationalExprNoIn - { $$ = new NotEqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new NotEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | EqualityExprNoIn STREQ RelationalExprNoIn - { $$ = new StrictEqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new StrictEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | EqualityExprNoIn STRNEQ RelationalExprNoIn - { $$ = new NotStrictEqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new NotStrictEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; EqualityExprNoBF: RelationalExprNoBF | EqualityExprNoBF EQEQ RelationalExpr - { $$ = new EqualNode($1, $3); } - | EqualityExprNoBF NE RelationalExpr { $$ = new NotEqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new EqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } + | EqualityExprNoBF NE RelationalExpr { $$ = createNodeInfo<ExpressionNode*>(new NotEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | EqualityExprNoBF STREQ RelationalExpr - { $$ = new StrictEqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new StrictEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } | EqualityExprNoBF STRNEQ RelationalExpr - { $$ = new NotStrictEqualNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new NotStrictEqualNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseANDExpr: EqualityExpr - | BitwiseANDExpr '&' EqualityExpr { $$ = new BitAndNode($1, $3); } + | BitwiseANDExpr '&' EqualityExpr { $$ = createNodeInfo<ExpressionNode*>(new BitAndNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseANDExprNoIn: EqualityExprNoIn | BitwiseANDExprNoIn '&' EqualityExprNoIn - { $$ = new BitAndNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new BitAndNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseANDExprNoBF: EqualityExprNoBF - | BitwiseANDExprNoBF '&' EqualityExpr { $$ = new BitAndNode($1, $3); } + | BitwiseANDExprNoBF '&' EqualityExpr { $$ = createNodeInfo<ExpressionNode*>(new BitAndNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseXORExpr: BitwiseANDExpr - | BitwiseXORExpr '^' BitwiseANDExpr { $$ = new BitXOrNode($1, $3); } + | BitwiseXORExpr '^' BitwiseANDExpr { $$ = createNodeInfo<ExpressionNode*>(new BitXOrNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseXORExprNoIn: BitwiseANDExprNoIn | BitwiseXORExprNoIn '^' BitwiseANDExprNoIn - { $$ = new BitXOrNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new BitXOrNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseXORExprNoBF: BitwiseANDExprNoBF | BitwiseXORExprNoBF '^' BitwiseANDExpr - { $$ = new BitXOrNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new BitXOrNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseORExpr: BitwiseXORExpr - | BitwiseORExpr '|' BitwiseXORExpr { $$ = new BitOrNode($1, $3); } + | BitwiseORExpr '|' BitwiseXORExpr { $$ = createNodeInfo<ExpressionNode*>(new BitOrNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseORExprNoIn: BitwiseXORExprNoIn | BitwiseORExprNoIn '|' BitwiseXORExprNoIn - { $$ = new BitOrNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new BitOrNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; BitwiseORExprNoBF: BitwiseXORExprNoBF | BitwiseORExprNoBF '|' BitwiseXORExpr - { $$ = new BitOrNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new BitOrNode(GLOBAL_DATA, $1.m_node, $3.m_node, $3.m_features & AssignFeature), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; LogicalANDExpr: BitwiseORExpr - | LogicalANDExpr AND BitwiseORExpr { $$ = new LogicalAndNode($1, $3); } + | LogicalANDExpr AND BitwiseORExpr { $$ = createNodeInfo<ExpressionNode*>(new LogicalOpNode(GLOBAL_DATA, $1.m_node, $3.m_node, OpLogicalAnd), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; LogicalANDExprNoIn: BitwiseORExprNoIn | LogicalANDExprNoIn AND BitwiseORExprNoIn - { $$ = new LogicalAndNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new LogicalOpNode(GLOBAL_DATA, $1.m_node, $3.m_node, OpLogicalAnd), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; LogicalANDExprNoBF: BitwiseORExprNoBF | LogicalANDExprNoBF AND BitwiseORExpr - { $$ = new LogicalAndNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new LogicalOpNode(GLOBAL_DATA, $1.m_node, $3.m_node, OpLogicalAnd), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; LogicalORExpr: LogicalANDExpr - | LogicalORExpr OR LogicalANDExpr { $$ = new LogicalOrNode($1, $3); } + | LogicalORExpr OR LogicalANDExpr { $$ = createNodeInfo<ExpressionNode*>(new LogicalOpNode(GLOBAL_DATA, $1.m_node, $3.m_node, OpLogicalOr), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; LogicalORExprNoIn: LogicalANDExprNoIn | LogicalORExprNoIn OR LogicalANDExprNoIn - { $$ = new LogicalOrNode($1, $3); } + { $$ = createNodeInfo<ExpressionNode*>(new LogicalOpNode(GLOBAL_DATA, $1.m_node, $3.m_node, OpLogicalOr), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; LogicalORExprNoBF: LogicalANDExprNoBF - | LogicalORExprNoBF OR LogicalANDExpr { $$ = new LogicalOrNode($1, $3); } + | LogicalORExprNoBF OR LogicalANDExpr { $$ = createNodeInfo<ExpressionNode*>(new LogicalOpNode(GLOBAL_DATA, $1.m_node, $3.m_node, OpLogicalOr), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; ConditionalExpr: LogicalORExpr | LogicalORExpr '?' AssignmentExpr ':' AssignmentExpr - { $$ = new ConditionalNode($1, $3, $5); } + { $$ = createNodeInfo<ExpressionNode*>(new ConditionalNode(GLOBAL_DATA, $1.m_node, $3.m_node, $5.m_node), $1.m_features | $3.m_features | $5.m_features, $1.m_numConstants + $3.m_numConstants + $5.m_numConstants); } ; ConditionalExprNoIn: LogicalORExprNoIn | LogicalORExprNoIn '?' AssignmentExprNoIn ':' AssignmentExprNoIn - { $$ = new ConditionalNode($1, $3, $5); } + { $$ = createNodeInfo<ExpressionNode*>(new ConditionalNode(GLOBAL_DATA, $1.m_node, $3.m_node, $5.m_node), $1.m_features | $3.m_features | $5.m_features, $1.m_numConstants + $3.m_numConstants + $5.m_numConstants); } ; ConditionalExprNoBF: LogicalORExprNoBF | LogicalORExprNoBF '?' AssignmentExpr ':' AssignmentExpr - { $$ = new ConditionalNode($1, $3, $5); } + { $$ = createNodeInfo<ExpressionNode*>(new ConditionalNode(GLOBAL_DATA, $1.m_node, $3.m_node, $5.m_node), $1.m_features | $3.m_features | $5.m_features, $1.m_numConstants + $3.m_numConstants + $5.m_numConstants); } ; AssignmentExpr: ConditionalExpr | LeftHandSideExpr AssignmentOperator AssignmentExpr - { $$ = makeAssignNode($1, $2, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeAssignNode(GLOBAL_DATA, $1.m_node, $2, $3.m_node, $1.m_features & AssignFeature, $3.m_features & AssignFeature, + @1.first_column, @2.first_column + 1, @3.last_column), $1.m_features | $3.m_features | AssignFeature, $1.m_numConstants + $3.m_numConstants); + } ; AssignmentExprNoIn: ConditionalExprNoIn | LeftHandSideExpr AssignmentOperator AssignmentExprNoIn - { $$ = makeAssignNode($1, $2, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeAssignNode(GLOBAL_DATA, $1.m_node, $2, $3.m_node, $1.m_features & AssignFeature, $3.m_features & AssignFeature, + @1.first_column, @2.first_column + 1, @3.last_column), $1.m_features | $3.m_features | AssignFeature, $1.m_numConstants + $3.m_numConstants); + } ; AssignmentExprNoBF: ConditionalExprNoBF | LeftHandSideExprNoBF AssignmentOperator AssignmentExpr - { $$ = makeAssignNode($1, $2, $3); } + { $$ = createNodeInfo<ExpressionNode*>(makeAssignNode(GLOBAL_DATA, $1.m_node, $2, $3.m_node, $1.m_features & AssignFeature, $3.m_features & AssignFeature, + @1.first_column, @2.first_column + 1, @3.last_column), $1.m_features | $3.m_features | AssignFeature, $1.m_numConstants + $3.m_numConstants); + } ; AssignmentOperator: @@ -678,17 +775,17 @@ AssignmentOperator: Expr: AssignmentExpr - | Expr ',' AssignmentExpr { $$ = new CommaNode($1, $3); } + | Expr ',' AssignmentExpr { $$ = createNodeInfo<ExpressionNode*>(new CommaNode(GLOBAL_DATA, $1.m_node, $3.m_node), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; ExprNoIn: AssignmentExprNoIn - | ExprNoIn ',' AssignmentExprNoIn { $$ = new CommaNode($1, $3); } + | ExprNoIn ',' AssignmentExprNoIn { $$ = createNodeInfo<ExpressionNode*>(new CommaNode(GLOBAL_DATA, $1.m_node, $3.m_node), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; ExprNoBF: AssignmentExprNoBF - | ExprNoBF ',' AssignmentExpr { $$ = new CommaNode($1, $3); } + | ExprNoBF ',' AssignmentExpr { $$ = createNodeInfo<ExpressionNode*>(new CommaNode(GLOBAL_DATA, $1.m_node, $3.m_node), $1.m_features | $3.m_features, $1.m_numConstants + $3.m_numConstants); } ; Statement: @@ -711,96 +808,125 @@ Statement: ; Block: - '{' '}' { $$ = createNodeInfo<StatementNode*>(new BlockNode(0), 0, 0); + OPENBRACE CLOSEBRACE { $$ = createNodeDeclarationInfo<StatementNode*>(new BlockNode(GLOBAL_DATA, 0), 0, 0, 0, 0); DBG($$.m_node, @1, @2); } - | '{' SourceElements '}' { $$ = createNodeInfo<StatementNode*>(new BlockNode($2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations); + | OPENBRACE SourceElements CLOSEBRACE { $$ = createNodeDeclarationInfo<StatementNode*>(new BlockNode(GLOBAL_DATA, $2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @3); } ; VariableStatement: - VAR VariableDeclarationList ';' { $$ = createNodeInfo<StatementNode*>(makeVarStatementNode($2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations); + VAR VariableDeclarationList ';' { $$ = createNodeDeclarationInfo<StatementNode*>(makeVarStatementNode(GLOBAL_DATA, $2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @3); } - | VAR VariableDeclarationList error { $$ = createNodeInfo<StatementNode*>(makeVarStatementNode($2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations); + | VAR VariableDeclarationList error { $$ = createNodeDeclarationInfo<StatementNode*>(makeVarStatementNode(GLOBAL_DATA, $2.m_node), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } ; VariableDeclarationList: IDENT { $$.m_node = 0; - $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; - appendToVarDeclarationList($$.m_varDeclarations, *$1, 0); + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>(GLOBAL_DATA); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$1, 0); $$.m_funcDeclarations = 0; + $$.m_features = (*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0; + $$.m_numConstants = 0; } - | IDENT Initializer { $$.m_node = new AssignResolveNode(*$1, $2); - $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; - appendToVarDeclarationList($$.m_varDeclarations, *$1, DeclarationStacks::HasInitializer); + | IDENT Initializer { AssignResolveNode* node = new AssignResolveNode(GLOBAL_DATA, *$1, $2.m_node, $2.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.first_column + 1, @2.last_column); + $$.m_node = node; + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>(GLOBAL_DATA); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$1, DeclarationStacks::HasInitializer); $$.m_funcDeclarations = 0; + $$.m_features = ((*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $2.m_features; + $$.m_numConstants = $2.m_numConstants; } | VariableDeclarationList ',' IDENT { $$.m_node = $1.m_node; $$.m_varDeclarations = $1.m_varDeclarations; - appendToVarDeclarationList($$.m_varDeclarations, *$3, 0); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$3, 0); $$.m_funcDeclarations = 0; + $$.m_features = $1.m_features | ((*$3 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0); + $$.m_numConstants = $1.m_numConstants; } | VariableDeclarationList ',' IDENT Initializer - { $$.m_node = combineVarInitializers($1.m_node, new AssignResolveNode(*$3, $4)); + { AssignResolveNode* node = new AssignResolveNode(GLOBAL_DATA, *$3, $4.m_node, $4.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @3.first_column, @4.first_column + 1, @4.last_column); + $$.m_node = combineVarInitializers(GLOBAL_DATA, $1.m_node, node); $$.m_varDeclarations = $1.m_varDeclarations; - appendToVarDeclarationList($$.m_varDeclarations, *$3, DeclarationStacks::HasInitializer); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$3, DeclarationStacks::HasInitializer); $$.m_funcDeclarations = 0; + $$.m_features = $1.m_features | ((*$3 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $4.m_features; + $$.m_numConstants = $1.m_numConstants + $4.m_numConstants; } ; VariableDeclarationListNoIn: IDENT { $$.m_node = 0; - $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; - appendToVarDeclarationList($$.m_varDeclarations, *$1, 0); + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>(GLOBAL_DATA); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$1, 0); $$.m_funcDeclarations = 0; + $$.m_features = (*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0; + $$.m_numConstants = 0; } - | IDENT InitializerNoIn { $$.m_node = new AssignResolveNode(*$1, $2); - $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; - appendToVarDeclarationList($$.m_varDeclarations, *$1, DeclarationStacks::HasInitializer); + | IDENT InitializerNoIn { AssignResolveNode* node = new AssignResolveNode(GLOBAL_DATA, *$1, $2.m_node, $2.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.first_column + 1, @2.last_column); + $$.m_node = node; + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>(GLOBAL_DATA); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$1, DeclarationStacks::HasInitializer); $$.m_funcDeclarations = 0; + $$.m_features = ((*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $2.m_features; + $$.m_numConstants = $2.m_numConstants; } | VariableDeclarationListNoIn ',' IDENT { $$.m_node = $1.m_node; $$.m_varDeclarations = $1.m_varDeclarations; - appendToVarDeclarationList($$.m_varDeclarations, *$3, 0); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$3, 0); $$.m_funcDeclarations = 0; + $$.m_features = $1.m_features | ((*$3 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0); + $$.m_numConstants = $1.m_numConstants; } | VariableDeclarationListNoIn ',' IDENT InitializerNoIn - { $$.m_node = combineVarInitializers($1.m_node, new AssignResolveNode(*$3, $4)); + { AssignResolveNode* node = new AssignResolveNode(GLOBAL_DATA, *$3, $4.m_node, $4.m_features & AssignFeature); + SET_EXCEPTION_LOCATION(node, @3.first_column, @4.first_column + 1, @4.last_column); + $$.m_node = combineVarInitializers(GLOBAL_DATA, $1.m_node, node); $$.m_varDeclarations = $1.m_varDeclarations; - appendToVarDeclarationList($$.m_varDeclarations, *$3, DeclarationStacks::HasInitializer); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, *$3, DeclarationStacks::HasInitializer); $$.m_funcDeclarations = 0; + $$.m_features = $1.m_features | ((*$3 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $4.m_features; + $$.m_numConstants = $1.m_numConstants + $4.m_numConstants; } ; ConstStatement: - CONSTTOKEN ConstDeclarationList ';' { $$ = createNodeInfo<StatementNode*>(new ConstStatementNode($2.m_node.head), $2.m_varDeclarations, $2.m_funcDeclarations); + CONSTTOKEN ConstDeclarationList ';' { $$ = createNodeDeclarationInfo<StatementNode*>(new ConstStatementNode(GLOBAL_DATA, $2.m_node.head), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @3); } | CONSTTOKEN ConstDeclarationList error - { $$ = createNodeInfo<StatementNode*>(new ConstStatementNode($2.m_node.head), $2.m_varDeclarations, $2.m_funcDeclarations); + { $$ = createNodeDeclarationInfo<StatementNode*>(new ConstStatementNode(GLOBAL_DATA, $2.m_node.head), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @2); AUTO_SEMICOLON; } ; ConstDeclarationList: - ConstDeclaration { $$.m_node.head = $1; + ConstDeclaration { $$.m_node.head = $1.m_node; $$.m_node.tail = $$.m_node.head; - $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>; - appendToVarDeclarationList($$.m_varDeclarations, $1); - $$.m_funcDeclarations = 0; } + $$.m_varDeclarations = new ParserRefCountedData<DeclarationStacks::VarStack>(GLOBAL_DATA); + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, $1.m_node); + $$.m_funcDeclarations = 0; + $$.m_features = $1.m_features; + $$.m_numConstants = $1.m_numConstants; + } | ConstDeclarationList ',' ConstDeclaration { $$.m_node.head = $1.m_node.head; - $1.m_node.tail->m_next = $3; - $$.m_node.tail = $3; + $1.m_node.tail->m_next = $3.m_node; + $$.m_node.tail = $3.m_node; $$.m_varDeclarations = $1.m_varDeclarations; - appendToVarDeclarationList($$.m_varDeclarations, $3); - $$.m_funcDeclarations = 0; } + appendToVarDeclarationList(GLOBAL_DATA, $$.m_varDeclarations, $3.m_node); + $$.m_funcDeclarations = 0; + $$.m_features = $1.m_features | $3.m_features; + $$.m_numConstants = $1.m_numConstants + $3.m_numConstants; } ; ConstDeclaration: - IDENT { $$ = new ConstDeclNode(*$1, 0); } - | IDENT Initializer { $$ = new ConstDeclNode(*$1, $2); } + IDENT { $$ = createNodeInfo<ConstDeclNode*>(new ConstDeclNode(GLOBAL_DATA, *$1, 0), (*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0, 0); } + | IDENT Initializer { $$ = createNodeInfo<ConstDeclNode*>(new ConstDeclNode(GLOBAL_DATA, *$1, $2.m_node), ((*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $2.m_features, $2.m_numConstants); } ; Initializer: @@ -812,198 +938,275 @@ InitializerNoIn: ; EmptyStatement: - ';' { $$ = createNodeInfo<StatementNode*>(new EmptyStatementNode(), 0, 0); } + ';' { $$ = createNodeDeclarationInfo<StatementNode*>(new EmptyStatementNode(GLOBAL_DATA), 0, 0, 0, 0); } ; ExprStatement: - ExprNoBF ';' { $$ = createNodeInfo<StatementNode*>(new ExprStatementNode($1), 0, 0); + ExprNoBF ';' { $$ = createNodeDeclarationInfo<StatementNode*>(new ExprStatementNode(GLOBAL_DATA, $1.m_node), 0, 0, $1.m_features, $1.m_numConstants); DBG($$.m_node, @1, @2); } - | ExprNoBF error { $$ = createNodeInfo<StatementNode*>(new ExprStatementNode($1), 0, 0); + | ExprNoBF error { $$ = createNodeDeclarationInfo<StatementNode*>(new ExprStatementNode(GLOBAL_DATA, $1.m_node), 0, 0, $1.m_features, $1.m_numConstants); 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); + { $$ = createNodeDeclarationInfo<StatementNode*>(new IfNode(GLOBAL_DATA, $3.m_node, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations, $3.m_features | $5.m_features, $3.m_numConstants + $5.m_numConstants); 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)); + { $$ = createNodeDeclarationInfo<StatementNode*>(new IfElseNode(GLOBAL_DATA, $3.m_node, $5.m_node, $7.m_node), + mergeDeclarationLists($5.m_varDeclarations, $7.m_varDeclarations), mergeDeclarationLists($5.m_funcDeclarations, $7.m_funcDeclarations), + $3.m_features | $5.m_features | $7.m_features, + $3.m_numConstants + $5.m_numConstants + $7.m_numConstants); DBG($$.m_node, @1, @4); } ; IterationStatement: - DO Statement WHILE '(' Expr ')' ';' { $$ = createNodeInfo<StatementNode*>(new DoWhileNode($2.m_node, $5), $2.m_varDeclarations, $2.m_funcDeclarations); + DO Statement WHILE '(' Expr ')' ';' { $$ = createNodeDeclarationInfo<StatementNode*>(new DoWhileNode(GLOBAL_DATA, $2.m_node, $5.m_node), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features | $5.m_features, $2.m_numConstants + $5.m_numConstants); DBG($$.m_node, @1, @3); } - | DO Statement WHILE '(' Expr ')' error { $$ = createNodeInfo<StatementNode*>(new DoWhileNode($2.m_node, $5), $2.m_varDeclarations, $2.m_funcDeclarations); + | DO Statement WHILE '(' Expr ')' error { $$ = createNodeDeclarationInfo<StatementNode*>(new DoWhileNode(GLOBAL_DATA, $2.m_node, $5.m_node), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features | $5.m_features, $2.m_numConstants + $5.m_numConstants); 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); + | WHILE '(' Expr ')' Statement { $$ = createNodeDeclarationInfo<StatementNode*>(new WhileNode(GLOBAL_DATA, $3.m_node, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations, $3.m_features | $5.m_features, $3.m_numConstants + $5.m_numConstants); 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); + { $$ = createNodeDeclarationInfo<StatementNode*>(new ForNode(GLOBAL_DATA, $3.m_node, $5.m_node, $7.m_node, $9.m_node, false), $9.m_varDeclarations, $9.m_funcDeclarations, + $3.m_features | $5.m_features | $7.m_features | $9.m_features, + $3.m_numConstants + $5.m_numConstants + $7.m_numConstants + $9.m_numConstants); 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)); + { $$ = createNodeDeclarationInfo<StatementNode*>(new ForNode(GLOBAL_DATA, $4.m_node, $6.m_node, $8.m_node, $10.m_node, true), + mergeDeclarationLists($4.m_varDeclarations, $10.m_varDeclarations), + mergeDeclarationLists($4.m_funcDeclarations, $10.m_funcDeclarations), + $4.m_features | $6.m_features | $8.m_features | $10.m_features, + $4.m_numConstants + $6.m_numConstants + $8.m_numConstants + $10.m_numConstants); 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); + ForInNode* node = new ForInNode(GLOBAL_DATA, $3.m_node, $5.m_node, $7.m_node); + SET_EXCEPTION_LOCATION(node, @3.first_column, @3.last_column, @5.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, $7.m_varDeclarations, $7.m_funcDeclarations, + $3.m_features | $5.m_features | $7.m_features, + $3.m_numConstants + $5.m_numConstants + $7.m_numConstants); 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); + { ForInNode *forIn = new ForInNode(GLOBAL_DATA, *$4, 0, $6.m_node, $8.m_node, @5.first_column, @5.first_column - @4.first_column, @6.last_column - @5.first_column); + SET_EXCEPTION_LOCATION(forIn, @4.first_column, @5.first_column + 1, @6.last_column); + appendToVarDeclarationList(GLOBAL_DATA, $8.m_varDeclarations, *$4, DeclarationStacks::HasInitializer); + $$ = createNodeDeclarationInfo<StatementNode*>(forIn, $8.m_varDeclarations, $8.m_funcDeclarations, ((*$4 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $6.m_features | $8.m_features, $6.m_numConstants + $8.m_numConstants); 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); + { ForInNode *forIn = new ForInNode(GLOBAL_DATA, *$4, $5.m_node, $7.m_node, $9.m_node, @5.first_column, @5.first_column - @4.first_column, @5.last_column - @5.first_column); + SET_EXCEPTION_LOCATION(forIn, @4.first_column, @6.first_column + 1, @7.last_column); + appendToVarDeclarationList(GLOBAL_DATA, $9.m_varDeclarations, *$4, DeclarationStacks::HasInitializer); + $$ = createNodeDeclarationInfo<StatementNode*>(forIn, $9.m_varDeclarations, $9.m_funcDeclarations, + ((*$4 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $5.m_features | $7.m_features | $9.m_features, + $5.m_numConstants + $7.m_numConstants + $9.m_numConstants); DBG($$.m_node, @1, @8); } ; ExprOpt: - /* nothing */ { $$ = 0; } + /* nothing */ { $$ = createNodeInfo<ExpressionNode*>(0, 0, 0); } | Expr ; ExprNoInOpt: - /* nothing */ { $$ = 0; } + /* nothing */ { $$ = createNodeInfo<ExpressionNode*>(0, 0, 0); } | ExprNoIn ; ContinueStatement: - CONTINUE ';' { $$ = createNodeInfo<StatementNode*>(new ContinueNode(), 0, 0); + CONTINUE ';' { ContinueNode* node = new ContinueNode(GLOBAL_DATA); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @1.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @2); } - | CONTINUE error { $$ = createNodeInfo<StatementNode*>(new ContinueNode(), 0, 0); + | CONTINUE error { ContinueNode* node = new ContinueNode(GLOBAL_DATA); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @1.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } - | CONTINUE IDENT ';' { $$ = createNodeInfo<StatementNode*>(new ContinueNode(*$2), 0, 0); + | CONTINUE IDENT ';' { ContinueNode* node = new ContinueNode(GLOBAL_DATA, *$2); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @3); } - | CONTINUE IDENT error { $$ = createNodeInfo<StatementNode*>(new ContinueNode(*$2), 0, 0); + | CONTINUE IDENT error { ContinueNode* node = new ContinueNode(GLOBAL_DATA, *$2); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 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; } + BREAK ';' { BreakNode* node = new BreakNode(GLOBAL_DATA); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @1.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @2); } + | BREAK error { BreakNode* node = new BreakNode(GLOBAL_DATA); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @1.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(new BreakNode(GLOBAL_DATA), 0, 0, 0, 0); DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } + | BREAK IDENT ';' { BreakNode* node = new BreakNode(GLOBAL_DATA, *$2); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @3); } + | BREAK IDENT error { BreakNode* node = new BreakNode(GLOBAL_DATA, *$2); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(new BreakNode(GLOBAL_DATA, *$2), 0, 0, 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; } + RETURN ';' { ReturnNode* node = new ReturnNode(GLOBAL_DATA, 0); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @1.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @2); } + | RETURN error { ReturnNode* node = new ReturnNode(GLOBAL_DATA, 0); + SET_EXCEPTION_LOCATION(node, @1.first_column, @1.last_column, @1.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, 0, 0); DBG($$.m_node, @1, @1); AUTO_SEMICOLON; } + | RETURN Expr ';' { ReturnNode* node = new ReturnNode(GLOBAL_DATA, $2.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @3); } + | RETURN Expr error { ReturnNode* node = new ReturnNode(GLOBAL_DATA, $2.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, $2.m_features, $2.m_numConstants); 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); + WITH '(' Expr ')' Statement { $$ = createNodeDeclarationInfo<StatementNode*>(new WithNode(GLOBAL_DATA, $3.m_node, $5.m_node, @3.last_column, @3.last_column - @3.first_column), + $5.m_varDeclarations, $5.m_funcDeclarations, $3.m_features | $5.m_features | WithFeature, $3.m_numConstants + $5.m_numConstants); DBG($$.m_node, @1, @4); } ; SwitchStatement: - SWITCH '(' Expr ')' CaseBlock { $$ = createNodeInfo<StatementNode*>(new SwitchNode($3, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations); + SWITCH '(' Expr ')' CaseBlock { $$ = createNodeDeclarationInfo<StatementNode*>(new SwitchNode(GLOBAL_DATA, $3.m_node, $5.m_node), $5.m_varDeclarations, $5.m_funcDeclarations, + $3.m_features | $5.m_features, $3.m_numConstants + $5.m_numConstants); 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)); } + OPENBRACE CaseClausesOpt CLOSEBRACE { $$ = createNodeDeclarationInfo<CaseBlockNode*>(new CaseBlockNode(GLOBAL_DATA, $2.m_node.head, 0, 0), $2.m_varDeclarations, $2.m_funcDeclarations, $2.m_features, $2.m_numConstants); } + | OPENBRACE CaseClausesOpt DefaultClause CaseClausesOpt CLOSEBRACE + { $$ = createNodeDeclarationInfo<CaseBlockNode*>(new CaseBlockNode(GLOBAL_DATA, $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), + $2.m_features | $3.m_features | $4.m_features, + $2.m_numConstants + $3.m_numConstants + $4.m_numConstants); } ; CaseClausesOpt: - /* nothing */ { $$.m_node.head = 0; $$.m_node.tail = 0; $$.m_varDeclarations = 0; $$.m_funcDeclarations = 0; } +/* nothing */ { $$.m_node.head = 0; $$.m_node.tail = 0; $$.m_varDeclarations = 0; $$.m_funcDeclarations = 0; $$.m_features = 0; $$.m_numConstants = 0; } | CaseClauses ; CaseClauses: - CaseClause { $$.m_node.head = new ClauseListNode($1.m_node); + CaseClause { $$.m_node.head = new ClauseListNode(GLOBAL_DATA, $1.m_node); $$.m_node.tail = $$.m_node.head; $$.m_varDeclarations = $1.m_varDeclarations; - $$.m_funcDeclarations = $1.m_funcDeclarations; } + $$.m_funcDeclarations = $1.m_funcDeclarations; + $$.m_features = $1.m_features; + $$.m_numConstants = $1.m_numConstants; } | CaseClauses CaseClause { $$.m_node.head = $1.m_node.head; - $$.m_node.tail = new ClauseListNode($1.m_node.tail, $2.m_node); + $$.m_node.tail = new ClauseListNode(GLOBAL_DATA, $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); + $$.m_features = $1.m_features | $2.m_features; + $$.m_numConstants = $1.m_numConstants + $2.m_numConstants; } ; 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); } + CASE Expr ':' { $$ = createNodeDeclarationInfo<CaseClauseNode*>(new CaseClauseNode(GLOBAL_DATA, $2.m_node), 0, 0, $2.m_features, $2.m_numConstants); } + | CASE Expr ':' SourceElements { $$ = createNodeDeclarationInfo<CaseClauseNode*>(new CaseClauseNode(GLOBAL_DATA, $2.m_node, $4.m_node), $4.m_varDeclarations, $4.m_funcDeclarations, $2.m_features | $4.m_features, $2.m_numConstants + $4.m_numConstants); } ; 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); } + DEFAULT ':' { $$ = createNodeDeclarationInfo<CaseClauseNode*>(new CaseClauseNode(GLOBAL_DATA, 0), 0, 0, 0, 0); } + | DEFAULT ':' SourceElements { $$ = createNodeDeclarationInfo<CaseClauseNode*>(new CaseClauseNode(GLOBAL_DATA, 0, $3.m_node), $3.m_varDeclarations, $3.m_funcDeclarations, $3.m_features, $3.m_numConstants); } ; LabelledStatement: - IDENT ':' Statement { $3.m_node->pushLabel(*$1); - $$ = createNodeInfo<StatementNode*>(new LabelNode(*$1, $3.m_node), $3.m_varDeclarations, $3.m_funcDeclarations); } + IDENT ':' Statement { LabelNode* node = new LabelNode(GLOBAL_DATA, *$1, $3.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, $3.m_varDeclarations, $3.m_funcDeclarations, $3.m_features, $3.m_numConstants); } ; 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; } + THROW Expr ';' { ThrowNode* node = new ThrowNode(GLOBAL_DATA, $2.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, $2.m_features, $2.m_numConstants); DBG($$.m_node, @1, @2); + } + | THROW Expr error { ThrowNode* node = new ThrowNode(GLOBAL_DATA, $2.m_node); + SET_EXCEPTION_LOCATION(node, @1.first_column, @2.last_column, @2.last_column); + $$ = createNodeDeclarationInfo<StatementNode*>(node, 0, 0, $2.m_features, $2.m_numConstants); 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)); + TRY Block FINALLY Block { $$ = createNodeDeclarationInfo<StatementNode*>(new TryNode(GLOBAL_DATA, $2.m_node, GLOBAL_DATA->propertyNames->nullIdentifier, 0, $4.m_node), + mergeDeclarationLists($2.m_varDeclarations, $4.m_varDeclarations), + mergeDeclarationLists($2.m_funcDeclarations, $4.m_funcDeclarations), + $2.m_features | $4.m_features, + $2.m_numConstants + $4.m_numConstants); 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)); + | TRY Block CATCH '(' IDENT ')' Block { $$ = createNodeDeclarationInfo<StatementNode*>(new TryNode(GLOBAL_DATA, $2.m_node, *$5, $7.m_node, 0), + mergeDeclarationLists($2.m_varDeclarations, $7.m_varDeclarations), + mergeDeclarationLists($2.m_funcDeclarations, $7.m_funcDeclarations), + $2.m_features | $7.m_features | CatchFeature, + $2.m_numConstants + $7.m_numConstants); 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)); + { $$ = createNodeDeclarationInfo<StatementNode*>(new TryNode(GLOBAL_DATA, $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), + $2.m_features | $7.m_features | $9.m_features | CatchFeature, + $2.m_numConstants + $7.m_numConstants + $9.m_numConstants); DBG($$.m_node, @1, @2); } ; DebuggerStatement: - DEBUGGER ';' { $$ = createNodeInfo<StatementNode*>(new EmptyStatementNode(), 0, 0); + DEBUGGER ';' { $$ = createNodeDeclarationInfo<StatementNode*>(new DebuggerStatementNode(GLOBAL_DATA), 0, 0, 0, 0); DBG($$.m_node, @1, @2); } - | DEBUGGER error { $$ = createNodeInfo<StatementNode*>(new EmptyStatementNode(), 0, 0); + | DEBUGGER error { $$ = createNodeDeclarationInfo<StatementNode*>(new DebuggerStatementNode(GLOBAL_DATA), 0, 0, 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); } + FUNCTION IDENT '(' ')' OPENBRACE FunctionBody CLOSEBRACE { $$ = createNodeInfo(new FuncDeclNode(GLOBAL_DATA, *$2, $6, LEXER->sourceCode($5, $7, @5.first_line)), ((*$2 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | ClosureFeature, 0); DBG($6, @5, @7); } + | FUNCTION IDENT '(' FormalParameterList ')' OPENBRACE FunctionBody CLOSEBRACE + { + $$ = createNodeInfo(new FuncDeclNode(GLOBAL_DATA, *$2, $7, LEXER->sourceCode($6, $8, @6.first_line), $4.m_node.head), ((*$2 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0) | $4.m_features | ClosureFeature, 0); + if ($4.m_features & ArgumentsFeature) + $7->setUsesArguments(); + 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); } + FUNCTION '(' ')' OPENBRACE FunctionBody CLOSEBRACE { $$ = createNodeInfo(new FuncExprNode(GLOBAL_DATA, GLOBAL_DATA->propertyNames->nullIdentifier, $5, LEXER->sourceCode($4, $6, @4.first_line)), ClosureFeature, 0); DBG($5, @4, @6); } + | FUNCTION '(' FormalParameterList ')' OPENBRACE FunctionBody CLOSEBRACE + { + $$ = createNodeInfo(new FuncExprNode(GLOBAL_DATA, GLOBAL_DATA->propertyNames->nullIdentifier, $6, LEXER->sourceCode($5, $7, @5.first_line), $3.m_node.head), $3.m_features | ClosureFeature, 0); + if ($3.m_features & ArgumentsFeature) + $6->setUsesArguments(); + DBG($6, @5, @7); + } + | FUNCTION IDENT '(' ')' OPENBRACE FunctionBody CLOSEBRACE { $$ = createNodeInfo(new FuncExprNode(GLOBAL_DATA, *$2, $6, LEXER->sourceCode($5, $7, @5.first_line)), ClosureFeature, 0); DBG($6, @5, @7); } + | FUNCTION IDENT '(' FormalParameterList ')' OPENBRACE FunctionBody CLOSEBRACE + { + $$ = createNodeInfo(new FuncExprNode(GLOBAL_DATA, *$2, $7, LEXER->sourceCode($6, $8, @6.first_line), $4.m_node.head), $4.m_features | ClosureFeature, 0); + if ($4.m_features & ArgumentsFeature) + $7->setUsesArguments(); + DBG($7, @6, @8); + } ; FormalParameterList: - IDENT { $$.head = new ParameterNode(*$1); - $$.tail = $$.head; } - | FormalParameterList ',' IDENT { $$.head = $1.head; - $$.tail = new ParameterNode($1.tail, *$3); } + IDENT { $$.m_node.head = new ParameterNode(GLOBAL_DATA, *$1); + $$.m_features = (*$1 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0; + $$.m_node.tail = $$.m_node.head; } + | FormalParameterList ',' IDENT { $$.m_node.head = $1.m_node.head; + $$.m_features = $1.m_features | ((*$3 == GLOBAL_DATA->propertyNames->arguments) ? ArgumentsFeature : 0); + $$.m_node.tail = new ParameterNode(GLOBAL_DATA, $1.m_node.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); + /* not in spec */ { $$ = FunctionBodyNode::create(GLOBAL_DATA, 0, 0, 0, NoFeatures, 0); } + | SourceElements { $$ = FunctionBodyNode::create(GLOBAL_DATA, $1.m_node, $1.m_varDeclarations ? &$1.m_varDeclarations->data : 0, + $1.m_funcDeclarations ? &$1.m_funcDeclarations->data : 0, + $1.m_features, $1.m_numConstants); // As in mergeDeclarationLists() we have to ref/deref to safely get rid of // the declaration lists. if ($1.m_varDeclarations) { @@ -1018,185 +1221,166 @@ FunctionBody: ; 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); } + /* not in spec */ { GLOBAL_DATA->parser->didFinishParsing(new SourceElements(GLOBAL_DATA), 0, 0, NoFeatures, @0.last_line, 0); } + | SourceElements { GLOBAL_DATA->parser->didFinishParsing($1.m_node, $1.m_varDeclarations, $1.m_funcDeclarations, $1.m_features, + @1.last_line, $1.m_numConstants); } ; SourceElements: - SourceElement { $$.m_node = new SourceElements; + SourceElement { $$.m_node = new SourceElements(GLOBAL_DATA); $$.m_node->append($1.m_node); $$.m_varDeclarations = $1.m_varDeclarations; $$.m_funcDeclarations = $1.m_funcDeclarations; + $$.m_features = $1.m_features; + $$.m_numConstants = $1.m_numConstants; } | 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); + $$.m_features = $1.m_features | $2.m_features; + $$.m_numConstants = $1.m_numConstants + $2.m_numConstants; } ; SourceElement: - FunctionDeclaration { $$ = createNodeInfo<StatementNode*>($1, 0, new ParserRefCountedData<DeclarationStacks::FunctionStack>); $$.m_funcDeclarations->data.append($1); } + FunctionDeclaration { $$ = createNodeDeclarationInfo<StatementNode*>($1.m_node, 0, new ParserRefCountedData<DeclarationStacks::FunctionStack>(GLOBAL_DATA), $1.m_features, 0); $$.m_funcDeclarations->data.append($1.m_node); } | 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) +static ExpressionNode* makeAssignNode(void* globalPtr, ExpressionNode* loc, Operator op, ExpressionNode* expr, bool locHasAssignments, bool exprHasAssignments, int start, int divot, int end) { if (!loc->isLocation()) - return new AssignErrorNode(loc, op, expr); + return new AssignErrorNode(GLOBAL_DATA, loc, op, expr, divot, divot - start, end - divot); 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 (op == OpEqual) { + AssignResolveNode* node = new AssignResolveNode(GLOBAL_DATA, resolve->identifier(), expr, exprHasAssignments); + SET_EXCEPTION_LOCATION(node, start, divot, end); + return node; + } else + return new ReadModifyResolveNode(GLOBAL_DATA, resolve->identifier(), op, expr, exprHasAssignments, divot, divot - start, end - divot); } 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); + return new AssignBracketNode(GLOBAL_DATA, bracket->base(), bracket->subscript(), expr, locHasAssignments, exprHasAssignments, bracket->divot(), bracket->divot() - start, end - bracket->divot()); + else { + ReadModifyBracketNode* node = new ReadModifyBracketNode(GLOBAL_DATA, bracket->base(), bracket->subscript(), op, expr, locHasAssignments, exprHasAssignments, divot, divot - start, end - divot); + node->setSubexpressionInfo(bracket->divot(), bracket->endOffset()); + return node; + } } 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); + return new AssignDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), expr, exprHasAssignments, dot->divot(), dot->divot() - start, end - dot->divot()); + + ReadModifyDotNode* node = new ReadModifyDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), op, expr, exprHasAssignments, divot, divot - start, end - divot); + node->setSubexpressionInfo(dot->divot(), dot->endOffset()); + return node; } -static ExpressionNode* makePrefixNode(ExpressionNode* expr, Operator op) -{ +static ExpressionNode* makePrefixNode(void* globalPtr, ExpressionNode* expr, Operator op, int start, int divot, int end) +{ if (!expr->isLocation()) - return new PrefixErrorNode(expr, op); + return new PrefixErrorNode(GLOBAL_DATA, expr, op, divot, divot - start, end - divot); if (expr->isResolveNode()) { ResolveNode* resolve = static_cast<ResolveNode*>(expr); - if (op == OpPlusPlus) - return new PreIncResolveNode(resolve->identifier()); - else - return new PreDecResolveNode(resolve->identifier()); + return new PrefixResolveNode(GLOBAL_DATA, resolve->identifier(), op, divot, divot - start, end - divot); } 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()); + PrefixBracketNode* node = new PrefixBracketNode(GLOBAL_DATA, bracket->base(), bracket->subscript(), op, divot, divot - start, end - divot); + node->setSubexpressionInfo(bracket->divot(), bracket->startOffset()); + return node; } 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()); + PrefixDotNode* node = new PrefixDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), op, divot, divot - start, end - divot); + node->setSubexpressionInfo(dot->divot(), dot->startOffset()); + return node; } -static ExpressionNode* makePostfixNode(ExpressionNode* expr, Operator op) +static ExpressionNode* makePostfixNode(void* globalPtr, ExpressionNode* expr, Operator op, int start, int divot, int end) { if (!expr->isLocation()) - return new PostfixErrorNode(expr, op); + return new PostfixErrorNode(GLOBAL_DATA, expr, op, divot, divot - start, end - divot); if (expr->isResolveNode()) { ResolveNode* resolve = static_cast<ResolveNode*>(expr); - if (op == OpPlusPlus) - return new PostIncResolveNode(resolve->identifier()); - else - return new PostDecResolveNode(resolve->identifier()); + return new PostfixResolveNode(GLOBAL_DATA, resolve->identifier(), op, divot, divot - start, end - divot); } 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()); + PostfixBracketNode* node = new PostfixBracketNode(GLOBAL_DATA, bracket->base(), bracket->subscript(), op, divot, divot - start, end - divot); + node->setSubexpressionInfo(bracket->divot(), bracket->endOffset()); + return node; + } 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()); + PostfixDotNode* node = new PostfixDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), op, divot, divot - start, end - divot); + node->setSubexpressionInfo(dot->divot(), dot->endOffset()); + return node; } -static ExpressionNode* makeFunctionCallNode(ExpressionNode* func, ArgumentsNode* args) +static ExpressionNodeInfo makeFunctionCallNode(void* globalPtr, ExpressionNodeInfo func, ArgumentsNodeInfo args, int start, int divot, int end) { - if (!func->isLocation()) - return new FunctionCallValueNode(func, args); - if (func->isResolveNode()) { - ResolveNode* resolve = static_cast<ResolveNode*>(func); - return new FunctionCallResolveNode(resolve->identifier(), args); + CodeFeatures features = func.m_features | args.m_features; + int numConstants = func.m_numConstants + args.m_numConstants; + if (!func.m_node->isLocation()) + return createNodeInfo<ExpressionNode*>(new FunctionCallValueNode(GLOBAL_DATA, func.m_node, args.m_node, divot, divot - start, end - divot), features, numConstants); + if (func.m_node->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(func.m_node); + const Identifier& identifier = resolve->identifier(); + if (identifier == GLOBAL_DATA->propertyNames->eval) + return createNodeInfo<ExpressionNode*>(new EvalFunctionCallNode(GLOBAL_DATA, args.m_node, divot, divot - start, end - divot), EvalFeature | features, numConstants); + return createNodeInfo<ExpressionNode*>(new FunctionCallResolveNode(GLOBAL_DATA, identifier, args.m_node, divot, divot - start, end - divot), features, numConstants); } - if (func->isBracketAccessorNode()) { - BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(func); - return new FunctionCallBracketNode(bracket->base(), bracket->subscript(), args); + if (func.m_node->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(func.m_node); + FunctionCallBracketNode* node = new FunctionCallBracketNode(GLOBAL_DATA, bracket->base(), bracket->subscript(), args.m_node, divot, divot - start, end - divot); + node->setSubexpressionInfo(bracket->divot(), bracket->endOffset()); + return createNodeInfo<ExpressionNode*>(node, features, numConstants); } - ASSERT(func->isDotAccessorNode()); - DotAccessorNode* dot = static_cast<DotAccessorNode*>(func); - return new FunctionCallDotNode(dot->base(), dot->identifier(), args); + ASSERT(func.m_node->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(func.m_node); + FunctionCallDotNode* node = new FunctionCallDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), args.m_node, divot, divot - start, end - divot); + node->setSubexpressionInfo(dot->divot(), dot->endOffset()); + return createNodeInfo<ExpressionNode*>(node, features, numConstants); } -static ExpressionNode* makeTypeOfNode(ExpressionNode* expr) +static ExpressionNode* makeTypeOfNode(void* globalPtr, ExpressionNode* expr) { if (expr->isResolveNode()) { ResolveNode* resolve = static_cast<ResolveNode*>(expr); - return new TypeOfResolveNode(resolve->identifier()); + return new TypeOfResolveNode(GLOBAL_DATA, resolve->identifier()); } - return new TypeOfValueNode(expr); + return new TypeOfValueNode(GLOBAL_DATA, expr); } -static ExpressionNode* makeDeleteNode(ExpressionNode* expr) +static ExpressionNode* makeDeleteNode(void* globalPtr, ExpressionNode* expr, int start, int divot, int end) { if (!expr->isLocation()) - return new DeleteValueNode(expr); + return new DeleteValueNode(GLOBAL_DATA, expr); if (expr->isResolveNode()) { ResolveNode* resolve = static_cast<ResolveNode*>(expr); - return new DeleteResolveNode(resolve->identifier()); + return new DeleteResolveNode(GLOBAL_DATA, resolve->identifier(), divot, divot - start, end - divot); } if (expr->isBracketAccessorNode()) { BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr); - return new DeleteBracketNode(bracket->base(), bracket->subscript()); + return new DeleteBracketNode(GLOBAL_DATA, bracket->base(), bracket->subscript(), divot, divot - start, end - divot); } ASSERT(expr->isDotAccessorNode()); DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr); - return new DeleteDotNode(dot->base(), dot->identifier()); + return new DeleteDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), divot, divot - start, end - divot); } -static PropertyNode* makeGetterOrSetterPropertyNode(const Identifier& getOrSet, const Identifier& name, ParameterNode* params, FunctionBodyNode* body) +static PropertyNode* makeGetterOrSetterPropertyNode(void* globalPtr, const Identifier& getOrSet, const Identifier& name, ParameterNode* params, FunctionBodyNode* body, const SourceCode& source) { PropertyNode::Type type; if (getOrSet == "get") @@ -1205,10 +1389,10 @@ static PropertyNode* makeGetterOrSetterPropertyNode(const Identifier& getOrSet, type = PropertyNode::Setter; else return 0; - return new PropertyNode(name, new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, body, params), type); + return new PropertyNode(GLOBAL_DATA, name, new FuncExprNode(GLOBAL_DATA, GLOBAL_DATA->propertyNames->nullIdentifier, body, source, params), type); } -static ExpressionNode* makeNegateNode(ExpressionNode* n) +static ExpressionNode* makeNegateNode(void* globalPtr, ExpressionNode* n) { if (n->isNumber()) { NumberNode* number = static_cast<NumberNode*>(n); @@ -1219,15 +1403,80 @@ static ExpressionNode* makeNegateNode(ExpressionNode* n) } } - return new NegateNode(n); + return new NegateNode(GLOBAL_DATA, n); } -static NumberNode* makeNumberNode(double d) +static NumberNode* makeNumberNode(void* globalPtr, double d) { JSValue* value = JSImmediate::from(d); if (value) - return new ImmediateNumberNode(value, d); - return new NumberNode(d); + return new ImmediateNumberNode(GLOBAL_DATA, value, d); + return new NumberNode(GLOBAL_DATA, d); +} + +static ExpressionNode* makeBitwiseNotNode(void* globalPtr, ExpressionNode* expr) +{ + if (expr->isNumber()) + return makeNumberNode(globalPtr, ~toInt32(static_cast<NumberNode*>(expr)->value())); + return new BitwiseNotNode(GLOBAL_DATA, expr); +} + +static ExpressionNode* makeMultNode(void* globalPtr, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) + return makeNumberNode(globalPtr, static_cast<NumberNode*>(expr1)->value() * static_cast<NumberNode*>(expr2)->value()); + + if (expr1->isNumber() && static_cast<NumberNode*>(expr1)->value() == 1) + return new UnaryPlusNode(GLOBAL_DATA, expr2); + + if (expr2->isNumber() && static_cast<NumberNode*>(expr2)->value() == 1) + return new UnaryPlusNode(GLOBAL_DATA, expr1); + + return new MultNode(GLOBAL_DATA, expr1, expr2, rightHasAssignments); +} + +static ExpressionNode* makeDivNode(void* globalPtr, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) + return makeNumberNode(globalPtr, static_cast<NumberNode*>(expr1)->value() / static_cast<NumberNode*>(expr2)->value()); + return new DivNode(GLOBAL_DATA, expr1, expr2, rightHasAssignments); +} + +static ExpressionNode* makeAddNode(void* globalPtr, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) + return makeNumberNode(globalPtr, static_cast<NumberNode*>(expr1)->value() + static_cast<NumberNode*>(expr2)->value()); + return new AddNode(GLOBAL_DATA, expr1, expr2, rightHasAssignments); +} + +static ExpressionNode* makeSubNode(void* globalPtr, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) + return makeNumberNode(globalPtr, static_cast<NumberNode*>(expr1)->value() - static_cast<NumberNode*>(expr2)->value()); + return new SubNode(GLOBAL_DATA, expr1, expr2, rightHasAssignments); +} + +static ExpressionNode* makeLeftShiftNode(void* globalPtr, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) + return makeNumberNode(globalPtr, toInt32(static_cast<NumberNode*>(expr1)->value()) << (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f)); + return new LeftShiftNode(GLOBAL_DATA, expr1, expr2, rightHasAssignments); +} + +static ExpressionNode* makeRightShiftNode(void* globalPtr, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) + return makeNumberNode(globalPtr, toInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f)); + return new RightShiftNode(GLOBAL_DATA, expr1, expr2, rightHasAssignments); } /* called by yyparse on error */ @@ -1237,25 +1486,26 @@ int yyerror(const char *) } /* may we automatically insert a semicolon ? */ -static bool allowAutomaticSemicolon() +static bool allowAutomaticSemicolon(Lexer& lexer, int yychar) { - return yychar == '}' || yychar == 0 || lexer().prevTerminator(); + return yychar == CLOSEBRACE || yychar == 0 || lexer.prevTerminator(); } -static ExpressionNode* combineVarInitializers(ExpressionNode* list, AssignResolveNode* init) +static ExpressionNode* combineVarInitializers(void* globalPtr, ExpressionNode* list, AssignResolveNode* init) { if (!list) return init; - return new VarDeclCommaNode(list, init); + return new VarDeclCommaNode(GLOBAL_DATA, 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) +static StatementNode* makeVarStatementNode(void* globalPtr, ExpressionNode* expr) { if (!expr) - return new EmptyStatementNode(); - return new VarStatementNode(expr); + return new EmptyStatementNode(GLOBAL_DATA); + return new VarStatementNode(GLOBAL_DATA, expr); } +#undef GLOBAL_DATA diff --git a/JavaScriptCore/kjs/identifier.cpp b/JavaScriptCore/kjs/identifier.cpp index 3fa01ad..50a6cc3 100644 --- a/JavaScriptCore/kjs/identifier.cpp +++ b/JavaScriptCore/kjs/identifier.cpp @@ -1,6 +1,5 @@ /* - * This file is part of the KDE libraries - * Copyright (C) 2003 Apple Computer, Inc + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,103 +19,103 @@ */ #include "config.h" - #include "identifier.h" -#include "JSLock.h" +#include "ExecState.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 { +namespace JSC { - template<typename T> struct DefaultHash; - template<typename T> struct StrHash; +typedef HashMap<const char*, RefPtr<UString::Rep>, PtrHash<const char*> > LiteralIdentifierTable; - 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; - }; +class IdentifierTable { +public: + ~IdentifierTable() + { + HashSet<UString::Rep*>::iterator end = m_table.end(); + for (HashSet<UString::Rep*>::iterator iter = m_table.begin(); iter != end; ++iter) + (*iter)->setIdentifierTable(0); + } + + std::pair<HashSet<UString::Rep*>::iterator, bool> add(UString::Rep* value) + { + std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add(value); + (*result.first)->setIdentifierTable(this); + return result; + } - template<> struct DefaultHash<KJS::UString::Rep *> { - typedef StrHash<KJS::UString::Rep *> Hash; - }; + template<typename U, typename V> + std::pair<HashSet<UString::Rep*>::iterator, bool> add(U value) + { + std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add<U, V>(value); + (*result.first)->setIdentifierTable(this); + return result; + } -} + void remove(UString::Rep* r) { m_table.remove(r); } -namespace KJS { + LiteralIdentifierTable& literalTable() { return m_literalTable; } -typedef HashSet<UString::Rep *> IdentifierTable; -static IdentifierTable *table; +private: + HashSet<UString::Rep*> m_table; + LiteralIdentifierTable m_literalTable; +}; -static inline IdentifierTable& identifierTable() +IdentifierTable* createIdentifierTable() { - ASSERT(JSLock::lockCount() > 0); - - if (!table) - table = new IdentifierTable; - return *table; + return new IdentifierTable; } +void deleteIdentifierTable(IdentifierTable* table) +{ + delete table; +} -bool Identifier::equal(const UString::Rep *r, const char *s) +bool Identifier::equal(const UString::Rep* r, const char* s) { int length = r->len; - const UChar *d = r->data(); + const UChar* d = r->data(); for (int i = 0; i != length; ++i) - if (d[i].uc != (unsigned char)s[i]) + if (d[i] != (unsigned char)s[i]) return false; return s[length] == 0; } -bool Identifier::equal(const UString::Rep *r, const UChar *s, int length) +bool Identifier::equal(const UString::Rep* r, const UChar* s, int length) { if (r->len != length) return false; - const UChar *d = r->data(); + 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) + if (d[i] != s[i]) return false; return true; } struct CStringTranslator { - static unsigned hash(const char *c) + static unsigned hash(const char* c) { return UString::Rep::computeHash(c); } - static bool equal(UString::Rep *r, const char *s) + 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) + 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)); + UChar* d = static_cast<UChar*>(fastMalloc(sizeof(UChar) * length)); for (size_t i = 0; i != length; i++) - d[i] = c[i]; + d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend - UString::Rep *r = UString::Rep::create(d, static_cast<int>(length)).releaseRef(); - r->isIdentifier = 1; + UString::Rep* r = UString::Rep::create(d, static_cast<int>(length)).releaseRef(); r->rc = 0; r->_hash = hash; @@ -124,23 +123,39 @@ struct CStringTranslator } }; -PassRefPtr<UString::Rep> Identifier::add(const char *c) +PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, 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; + if (!c[1]) + return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0]))); + + IdentifierTable& identifierTable = *globalData->identifierTable; + LiteralIdentifierTable& literalIdentifierTable = identifierTable.literalTable(); + + const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c); + if (iter != literalIdentifierTable.end()) + return iter->second; + + UString::Rep* addedString = *identifierTable.add<const char*, CStringTranslator>(c).first; + literalIdentifierTable.add(c, addedString); + + return addedString; +} + +PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const char* c) +{ + return add(&exec->globalData(), c); } struct UCharBuffer { - const UChar *s; + const UChar* s; unsigned int length; }; @@ -151,19 +166,18 @@ struct UCharBufferTranslator return UString::Rep::computeHash(buf.s, buf.length); } - static bool equal(UString::Rep *str, const UCharBuffer& buf) + 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) + static void translate(UString::Rep*& location, const UCharBuffer& buf, unsigned hash) { - UChar *d = static_cast<UChar *>(fastMalloc(sizeof(UChar) * buf.length)); + 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; + UString::Rep* r = UString::Rep::create(d, buf.length).releaseRef(); r->rc = 0; r->_hash = hash; @@ -171,35 +185,79 @@ struct UCharBufferTranslator } }; -PassRefPtr<UString::Rep> Identifier::add(const UChar *s, int length) +PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const UChar* s, int length) { + if (length == 1) { + UChar c = s[0]; + if (c <= 0xFF) + return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); + } if (!length) { UString::Rep::empty.hash(); return &UString::Rep::empty; } - UCharBuffer buf = {s, length}; - return *identifierTable().add<UCharBuffer, UCharBufferTranslator>(buf).first; + return *globalData->identifierTable->add<UCharBuffer, UCharBufferTranslator>(buf).first; } -PassRefPtr<UString::Rep> Identifier::addSlowCase(UString::Rep *r) +PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int length) { - ASSERT(!r->isIdentifier); + return add(&exec->globalData(), s, length); +} - if (r->len == 0) { +PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UString::Rep* r) +{ + ASSERT(!r->identifierTable()); + if (r->len == 1) { + UChar c = r->data()[0]; + if (c <= 0xFF) + r = globalData->smallStrings.singleCharacterStringRep(c); + if (r->identifierTable()) { +#ifndef NDEBUG + checkSameIdentifierTable(globalData, r); +#endif + return r; + } + } + if (!r->len) { UString::Rep::empty.hash(); return &UString::Rep::empty; } + return *globalData->identifierTable->add(r).first; +} + +PassRefPtr<UString::Rep> Identifier::addSlowCase(ExecState* exec, UString::Rep* r) +{ + return addSlowCase(&exec->globalData(), r); +} - UString::Rep *result = *identifierTable().add(r).first; - if (result == r) - r->isIdentifier = true; - return result; +void Identifier::remove(UString::Rep* r) +{ + r->identifierTable()->remove(r); } -void Identifier::remove(UString::Rep *r) +#ifndef NDEBUG + +void Identifier::checkSameIdentifierTable(ExecState* exec, UString::Rep* rep) { - identifierTable().remove(r); + ASSERT(rep->identifierTable() == exec->globalData().identifierTable); } -} // namespace KJS +void Identifier::checkSameIdentifierTable(JSGlobalData* globalData, UString::Rep* rep) +{ + ASSERT(rep->identifierTable() == globalData->identifierTable); +} + +#else + +void Identifier::checkSameIdentifierTable(ExecState*, UString::Rep*) +{ +} + +void Identifier::checkSameIdentifierTable(JSGlobalData*, UString::Rep*) +{ +} + +#endif + +} // namespace JSC diff --git a/JavaScriptCore/kjs/identifier.h b/JavaScriptCore/kjs/identifier.h index fc43344..a79dd92 100644 --- a/JavaScriptCore/kjs/identifier.h +++ b/JavaScriptCore/kjs/identifier.h @@ -21,31 +21,39 @@ #ifndef KJS_IDENTIFIER_H #define KJS_IDENTIFIER_H +#include "JSGlobalData.h" #include "ustring.h" -namespace KJS { +namespace JSC { + + class ExecState; class Identifier { - friend class PropertyMap; + friend class StructureID; 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())) { } + + Identifier(ExecState* exec, const char* s) : _ustring(add(exec, s)) { } // Only to be used with string literals. + Identifier(ExecState* exec, const UChar* s, int length) : _ustring(add(exec, s, length)) { } + Identifier(ExecState* exec, UString::Rep* rep) : _ustring(add(exec, rep)) { } + Identifier(ExecState* exec, const UString& s) : _ustring(add(exec, s.rep())) { } + + Identifier(JSGlobalData* globalData, const char* s) : _ustring(add(globalData, s)) { } // Only to be used with string literals. + Identifier(JSGlobalData* globalData, const UChar* s, int length) : _ustring(add(globalData, s, length)) { } + Identifier(JSGlobalData* globalData, UString::Rep* rep) : _ustring(add(globalData, rep)) { } + Identifier(JSGlobalData* globalData, const UString& s) : _ustring(add(globalData, 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)); } + static Identifier from(ExecState* exec, unsigned y) { return Identifier(exec, UString::from(y)); } bool isNull() const { return _ustring.isNull(); } bool isEmpty() const { return _ustring.isEmpty(); } @@ -61,40 +69,72 @@ namespace KJS { friend bool operator==(const Identifier&, const char*); - static void remove(UString::Rep* ); + 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*); + static bool equal(const UString::Rep* a, const UString::Rep* b) { return JSC::equal(a, b); } + + static PassRefPtr<UString::Rep> add(ExecState*, const char*); // Only to be used with string literals. + static PassRefPtr<UString::Rep> add(JSGlobalData*, const char*); // Only to be used with string literals. + + static void initializeIdentifierThreading(); 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) + 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(ExecState*, const UChar*, int length); + static PassRefPtr<UString::Rep> add(JSGlobalData*, const UChar*, int length); + + static PassRefPtr<UString::Rep> add(ExecState* exec, UString::Rep* r) + { + if (r->identifierTable()) { +#ifndef NDEBUG + checkSameIdentifierTable(exec, r); +#endif + return r; + } + return addSlowCase(exec, r); + } + static PassRefPtr<UString::Rep> add(JSGlobalData* globalData, UString::Rep* r) { - if (r->isIdentifier) + if (r->identifierTable()) { +#ifndef NDEBUG + checkSameIdentifierTable(globalData, r); +#endif return r; - return addSlowCase(r); + } + return addSlowCase(globalData, r); } - static PassRefPtr<UString::Rep> addSlowCase(UString::Rep *r); + + static PassRefPtr<UString::Rep> addSlowCase(ExecState*, UString::Rep* r); + static PassRefPtr<UString::Rep> addSlowCase(JSGlobalData*, UString::Rep* r); + + static void checkSameIdentifierTable(ExecState*, UString::Rep*); + static void checkSameIdentifierTable(JSGlobalData*, UString::Rep*); }; inline bool operator==(const Identifier& a, const Identifier& b) - { return Identifier::equal(a, b); } + { + return Identifier::equal(a, b); + } inline bool operator!=(const Identifier& a, const Identifier& b) - { return !Identifier::equal(a, b); } + { + return !Identifier::equal(a, b); + } inline bool operator==(const Identifier& a, const char* b) - { return Identifier::equal(a, b); } + { + return Identifier::equal(a, b); + } + + IdentifierTable* createIdentifierTable(); + void deleteIdentifierTable(IdentifierTable*); -} // namespace KJS +} // namespace JSC #endif // KJS_IDENTIFIER_H diff --git a/JavaScriptCore/kjs/internal.cpp b/JavaScriptCore/kjs/internal.cpp deleted file mode 100644 index 5482b48..0000000 --- a/JavaScriptCore/kjs/internal.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - * 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 deleted file mode 100644 index 51438b3..0000000 --- a/JavaScriptCore/kjs/internal.h +++ /dev/null @@ -1,122 +0,0 @@ -// -*- 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; - isAborted = false; - } - - void abort() { isAborted = true; } - bool aborted() const { return isAborted; } - - AttachedGlobalObject* globalObjects; - bool isAborted; - }; - -#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 index 0d515ac..1188349 100644 --- a/JavaScriptCore/kjs/interpreter.cpp +++ b/JavaScriptCore/kjs/interpreter.cpp @@ -1,6 +1,4 @@ -// -*- 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, 2007 Apple Inc. @@ -27,131 +25,54 @@ #include "ExecState.h" #include "JSGlobalObject.h" +#include "JSLock.h" +#include "Machine.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 "completion.h" +#include "Debugger.h" #include <stdio.h> -#include <wtf/Assertions.h> -namespace KJS { +#if !PLATFORM(WIN_OS) +#include <unistd.h> +#endif -Completion Interpreter::checkSyntax(ExecState* exec, const UString& sourceURL, int startingLineNumber, const UString& code) -{ - return checkSyntax(exec, sourceURL, startingLineNumber, code.data(), code.size()); -} +namespace JSC { -Completion Interpreter::checkSyntax(ExecState* exec, const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength) +Completion Interpreter::checkSyntax(ExecState* exec, const SourceCode& source) { - JSLock lock; + JSLock lock(exec); int errLine; UString errMsg; - RefPtr<ProgramNode> progNode = parser().parse<ProgramNode>(sourceURL, startingLineNumber, code, codeLength, 0, &errLine, &errMsg); + + RefPtr<ProgramNode> progNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); if (!progNode) - return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, 0, sourceURL)); + return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url())); 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) +Completion Interpreter::evaluate(ExecState* exec, ScopeChain& scopeChain, const SourceCode& source, JSValue* thisValue) { - JSLock lock; + JSLock lock(exec); - 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 - } + RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); - return res; -} + if (!programNode) + return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url())); -static bool printExceptions = false; + JSObject* thisObj = (!thisValue || thisValue->isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue->toObject(exec); -bool Interpreter::shouldPrintExceptions() -{ - return printExceptions; -} + JSValue* exception = noValue(); + JSValue* result = exec->machine()->execute(programNode.get(), exec, scopeChain.node(), thisObj, &exception); -void Interpreter::setShouldPrintExceptions(bool print) -{ - printExceptions = print; + if (exception) { + if (exception->isObject() && asObject(exception)->isWatchdogException()) + return Completion(Interrupted, result); + return Completion(Throw, exception); + } + return Completion(Normal, result); } -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/interpreter.h b/JavaScriptCore/kjs/interpreter.h index 79b9398..0366063 100644 --- a/JavaScriptCore/kjs/interpreter.h +++ b/JavaScriptCore/kjs/interpreter.h @@ -23,14 +23,16 @@ #ifndef KJS_Interpreter_h #define KJS_Interpreter_h -namespace KJS { +#include "JSValue.h" +#include <wtf/PassRefPtr.h> +#include <wtf/unicode/Unicode.h> + +namespace JSC { class Completion; class ExecState; - class JSValue; - class UString; - - struct UChar; + class ScopeChain; + class SourceCode; class Interpreter { public: @@ -41,8 +43,7 @@ namespace KJS { * @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); + static Completion checkSyntax(ExecState*, const SourceCode&); /** * Evaluates the supplied ECMAScript code. @@ -55,17 +56,13 @@ namespace KJS { * 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 + * @param thisValue 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); + static Completion evaluate(ExecState*, ScopeChain&, const SourceCode&, JSValue* thisValue = noValue()); }; -} // namespace KJS +} // namespace JSC #endif // KJS_Interpreter_h diff --git a/JavaScriptCore/kjs/jsc.pro b/JavaScriptCore/kjs/jsc.pro new file mode 100644 index 0000000..2a30d65 --- /dev/null +++ b/JavaScriptCore/kjs/jsc.pro @@ -0,0 +1,35 @@ +TEMPLATE = app +TARGET = jsc +DESTDIR = .. +SOURCES = Shell.cpp +QT -= gui +INCLUDEPATH += $$PWD/.. \ + $$PWD \ + $$PWD/../bindings \ + $$PWD/../bindings/c \ + $$PWD/../wtf \ + $$PWD/../VM +CONFIG -= app_bundle +DEFINES += BUILDING_QT__ +CONFIG += building-libs + +CONFIG(release) { + DEFINES += NDEBUG USE_SYSTEM_MALLOC +} + +include($$PWD/../../WebKit.pri) + +CONFIG += link_pkgconfig + +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) + +lessThan(QT_MINOR_VERSION, 4) { + DEFINES += QT_BEGIN_NAMESPACE="" QT_END_NAMESPACE="" +} diff --git a/JavaScriptCore/kjs/lexer.cpp b/JavaScriptCore/kjs/lexer.cpp index c4d327f..7313b12 100644 --- a/JavaScriptCore/kjs/lexer.cpp +++ b/JavaScriptCore/kjs/lexer.cpp @@ -1,7 +1,6 @@ -// -*- c-basic-offset: 2 -*- /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2006, 2007 Apple Inc. All Rights Reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * * This library is free software; you can redistribute it and/or @@ -25,9 +24,10 @@ #include "lexer.h" #include "dtoa.h" -#include "function.h" +#include "JSFunction.h" #include "nodes.h" #include "NodeInfo.h" +#include "JSGlobalObjectFunctions.h" #include <ctype.h> #include <limits.h> #include <string.h> @@ -38,7 +38,7 @@ using namespace WTF; using namespace Unicode; // we can't specify the namespace in yacc's C output, so do it here -using namespace KJS; +using namespace JSC; #ifndef KDE_USE_FINAL #include "grammar.h" @@ -47,46 +47,39 @@ using namespace KJS; #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() +int kjsyylex(void* lvalp, void* llocp, void* globalData) { - return lexer().lex(); + return static_cast<JSGlobalData*>(globalData)->lexer->lex(lvalp, llocp); } -namespace KJS { +namespace JSC { 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() +Lexer::Lexer(JSGlobalData* globalData) : 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_restrKeyword(false) + , m_eatNextIdentifier(false) + , m_stackToken(-1) + , m_lastToken(-1) + , m_position(0) + , m_code(0) + , m_length(0) + , m_atLineStart(true) + , m_current(0) + , m_next1(0) + , m_next2(0) + , m_next3(0) + , m_currentOffset(0) + , m_nextOffset1(0) + , m_nextOffset2(0) + , m_nextOffset3(0) + , m_globalData(globalData) + , m_mainTable(JSC::mainTable) { m_buffer8.reserveCapacity(initialReadBufferCapacity); m_buffer16.reserveCapacity(initialReadBufferCapacity); @@ -94,708 +87,746 @@ Lexer::Lexer() m_identifiers.reserveCapacity(initialStringTableCapacity); } -void Lexer::setCode(int startingLineNumber, const KJS::UChar *c, unsigned int len) +Lexer::~Lexer() +{ + m_mainTable.deleteTable(); +} + +void Lexer::setCode(const SourceCode& source) { - 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; + yylineno = source.firstLine(); + m_restrKeyword = false; + m_delimited = false; + m_eatNextIdentifier = false; + m_stackToken = -1; + m_lastToken = -1; + + m_position = 0; + m_source = &source; + m_code = source.data(); + m_length = source.length(); + m_skipLF = false; + m_skipCR = false; + m_error = false; + m_atLineStart = true; + + // read first characters + shift(4); } -void Lexer::shift(unsigned int p) +void Lexer::shift(unsigned 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; - } + // ECMA-262 calls for stripping Cf characters here, but we only do this for BOM, + // see <https://bugs.webkit.org/show_bug.cgi?id=4931>. + + while (p--) { + m_current = m_next1; + m_next1 = m_next2; + m_next2 = m_next3; + m_currentOffset = m_nextOffset1; + m_nextOffset1 = m_nextOffset2; + m_nextOffset2 = m_nextOffset3; + do { + if (m_position >= m_length) { + m_nextOffset3 = m_position; + m_position++; + m_next3 = -1; + break; + } + m_nextOffset3 = m_position; + m_next3 = m_code[m_position++]; + } while (m_next3 == 0xFEFF); + } } // called on each new line void Lexer::nextLine() { - yylineno++; - atLineStart = true; + yylineno++; + m_atLineStart = true; } void Lexer::setDone(State s) { - state = s; - done = true; + m_state = s; + m_done = true; } -int Lexer::lex() +int Lexer::lex(void* p1, void* p2) { - 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); + YYSTYPE* lvalp = static_cast<YYSTYPE*>(p1); + YYLTYPE* llocp = static_cast<YYLTYPE*>(p2); + int token = 0; + m_state = Start; + unsigned short stringType = 0; // either single or double quotes + m_buffer8.clear(); + m_buffer16.clear(); + m_done = false; + m_terminator = false; + m_skipLF = false; + m_skipCR = false; + + // did we push a token on the stack previously ? + // (after an automatic semicolon insertion) + if (m_stackToken >= 0) { + setDone(Other); + token = m_stackToken; + m_stackToken = 0; } - 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); + int startOffset = m_currentOffset; + while (!m_done) { + if (m_skipLF && m_current != '\n') // found \r but not \n afterwards + m_skipLF = false; + if (m_skipCR && m_current != '\r') // found \n but not \r afterwards + m_skipCR = false; + if (m_skipLF || m_skipCR) { // found \r\n or \n\r -> eat the second one + m_skipLF = false; + m_skipCR = false; + shift(1); } - } - 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); + switch (m_state) { + case Start: + startOffset = m_currentOffset; + if (isWhiteSpace()) { + // do nothing + } else if (m_current == '/' && m_next1 == '/') { + shift(1); + m_state = InSingleLineComment; + } else if (m_current == '/' && m_next1 == '*') { + shift(1); + m_state = InMultiLineComment; + } else if (m_current == -1) { + if (!m_terminator && !m_delimited) { + // automatic semicolon insertion if program incomplete + token = ';'; + m_stackToken = 0; + setDone(Other); + } else + setDone(Eof); + } else if (isLineTerminator()) { + nextLine(); + m_terminator = true; + if (m_restrKeyword) { + token = ';'; + setDone(Other); + } + } else if (m_current == '"' || m_current == '\'') { + m_state = InString; + stringType = static_cast<unsigned short>(m_current); + } else if (isIdentStart(m_current)) { + record16(m_current); + m_state = InIdentifierOrKeyword; + } else if (m_current == '\\') + m_state = InIdentifierStartUnicodeEscapeStart; + else if (m_current == '0') { + record8(m_current); + m_state = InNum0; + } else if (isDecimalDigit(m_current)) { + record8(m_current); + m_state = InNum; + } else if (m_current == '.' && isDecimalDigit(m_next1)) { + record8(m_current); + m_state = InDecimal; + // <!-- marks the beginning of a line comment (for www usage) + } else if (m_current == '<' && m_next1 == '!' && m_next2 == '-' && m_next3 == '-') { + shift(3); + m_state = InSingleLineComment; + // same for --> + } else if (m_atLineStart && m_current == '-' && m_next1 == '-' && m_next2 == '>') { + shift(2); + m_state = InSingleLineComment; + } else { + token = matchPunctuator(lvalp->intValue, m_current, m_next1, m_next2, m_next3); + if (token != -1) + setDone(Other); + else + setDone(Bad); + } + break; + case InString: + if (m_current == stringType) { + shift(1); + setDone(String); + } else if (isLineTerminator() || m_current == -1) + setDone(Bad); + else if (m_current == '\\') + m_state = InEscapeSequence; + else + record16(m_current); + break; + // Escape Sequences inside of strings + case InEscapeSequence: + if (isOctalDigit(m_current)) { + if (m_current >= '0' && m_current <= '3' && + isOctalDigit(m_next1) && isOctalDigit(m_next2)) { + record16(convertOctal(m_current, m_next1, m_next2)); + shift(2); + m_state = InString; + } else if (isOctalDigit(m_current) && isOctalDigit(m_next1)) { + record16(convertOctal('0', m_current, m_next1)); + shift(1); + m_state = InString; + } else if (isOctalDigit(m_current)) { + record16(convertOctal('0', '0', m_current)); + m_state = InString; + } else + setDone(Bad); + } else if (m_current == 'x') + m_state = InHexEscape; + else if (m_current == 'u') + m_state = InUnicodeEscape; + else if (isLineTerminator()) { + nextLine(); + m_state = InString; + } else { + record16(singleEscape(static_cast<unsigned short>(m_current))); + m_state = InString; + } + break; + case InHexEscape: + if (isHexDigit(m_current) && isHexDigit(m_next1)) { + m_state = InString; + record16(convertHex(m_current, m_next1)); + shift(1); + } else if (m_current == stringType) { + record16('x'); + shift(1); + setDone(String); + } else { + record16('x'); + record16(m_current); + m_state = InString; + } + break; + case InUnicodeEscape: + if (isHexDigit(m_current) && isHexDigit(m_next1) && isHexDigit(m_next2) && isHexDigit(m_next3)) { + record16(convertUnicode(m_current, m_next1, m_next2, m_next3)); + shift(3); + m_state = InString; + } else if (m_current == stringType) { + record16('u'); + shift(1); + setDone(String); + } else + setDone(Bad); + break; + case InSingleLineComment: + if (isLineTerminator()) { + nextLine(); + m_terminator = true; + if (m_restrKeyword) { + token = ';'; + setDone(Other); + } else + m_state = Start; + } else if (m_current == -1) + setDone(Eof); + break; + case InMultiLineComment: + if (m_current == -1) + setDone(Bad); + else if (isLineTerminator()) + nextLine(); + else if (m_current == '*' && m_next1 == '/') { + m_state = Start; + shift(1); + } + break; + case InIdentifierOrKeyword: + case InIdentifier: + if (isIdentPart(m_current)) + record16(m_current); + else if (m_current == '\\') + m_state = InIdentifierPartUnicodeEscapeStart; + else + setDone(m_state == InIdentifierOrKeyword ? IdentifierOrKeyword : Identifier); + break; + case InNum0: + if (m_current == 'x' || m_current == 'X') { + record8(m_current); + m_state = InHex; + } else if (m_current == '.') { + record8(m_current); + m_state = InDecimal; + } else if (m_current == 'e' || m_current == 'E') { + record8(m_current); + m_state = InExponentIndicator; + } else if (isOctalDigit(m_current)) { + record8(m_current); + m_state = InOctal; + } else if (isDecimalDigit(m_current)) { + record8(m_current); + m_state = InDecimal; + } else + setDone(Number); + break; + case InHex: + if (isHexDigit(m_current)) + record8(m_current); + else + setDone(Hex); + break; + case InOctal: + if (isOctalDigit(m_current)) + record8(m_current); + else if (isDecimalDigit(m_current)) { + record8(m_current); + m_state = InDecimal; + } else + setDone(Octal); + break; + case InNum: + if (isDecimalDigit(m_current)) + record8(m_current); + else if (m_current == '.') { + record8(m_current); + m_state = InDecimal; + } else if (m_current == 'e' || m_current == 'E') { + record8(m_current); + m_state = InExponentIndicator; + } else + setDone(Number); + break; + case InDecimal: + if (isDecimalDigit(m_current)) + record8(m_current); + else if (m_current == 'e' || m_current == 'E') { + record8(m_current); + m_state = InExponentIndicator; + } else + setDone(Number); + break; + case InExponentIndicator: + if (m_current == '+' || m_current == '-') + record8(m_current); + else if (isDecimalDigit(m_current)) { + record8(m_current); + m_state = InExponent; + } else + setDone(Bad); + break; + case InExponent: + if (isDecimalDigit(m_current)) + record8(m_current); + else + setDone(Number); + break; + case InIdentifierStartUnicodeEscapeStart: + if (m_current == 'u') + m_state = InIdentifierStartUnicodeEscape; + else + setDone(Bad); + break; + case InIdentifierPartUnicodeEscapeStart: + if (m_current == 'u') + m_state = InIdentifierPartUnicodeEscape; + else + setDone(Bad); + break; + case InIdentifierStartUnicodeEscape: + if (!isHexDigit(m_current) || !isHexDigit(m_next1) || !isHexDigit(m_next2) || !isHexDigit(m_next3)) { + setDone(Bad); + break; + } + token = convertUnicode(m_current, m_next1, m_next2, m_next3); + shift(3); + if (!isIdentStart(token)) { + setDone(Bad); + break; + } + record16(token); + m_state = InIdentifier; + break; + case InIdentifierPartUnicodeEscape: + if (!isHexDigit(m_current) || !isHexDigit(m_next1) || !isHexDigit(m_next2) || !isHexDigit(m_next3)) { + setDone(Bad); + break; + } + token = convertUnicode(m_current, m_next1, m_next2, m_next3); + shift(3); + if (!isIdentPart(token)) { + setDone(Bad); + break; + } + record16(token); + m_state = InIdentifier; + break; + default: + ASSERT(!"Unhandled state in switch statement"); } - } 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'); + + // move on to the next character + if (!m_done) + shift(1); + if (m_state != Start && m_state != InSingleLineComment) + m_atLineStart = false; + } + + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad + if ((m_state == Number || m_state == Octal || m_state == Hex) && isIdentStart(m_current)) + m_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()); + fprintf(stderr, "line: %d ", lineNo()); + fprintf(stderr, "yytext (%x): ", m_buffer8[0]); + fprintf(stderr, "%s ", m_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); - } + double dval = 0; + if (m_state == Number) + dval = strtod(m_buffer8.data(), 0L); + else if (m_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); + 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'; - } + m_state = Number; + } else if (m_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); + if (dval >= mantissaOverflowLowerBound) + dval = parseIntOverflow(m_buffer8.data() + 1, p - (m_buffer8.data() + 2), 8); - state = Number; - } + m_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)"); - } + switch (m_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: + if (m_state != Identifier) + m_eatNextIdentifier = false; + + m_restrKeyword = false; + m_delimited = false; + llocp->first_line = yylineno; + llocp->last_line = yylineno; + llocp->first_column = startOffset; + llocp->last_column = m_currentOffset; + switch (m_state) { + case Eof: + token = 0; + break; + case Other: + if (token == '}' || token == ';') + m_delimited = true; + break; + case Identifier: + // Apply anonymous-function hack below (eat the identifier). + if (m_eatNextIdentifier) { + m_eatNextIdentifier = false; + token = lex(lvalp, llocp); + break; + } + lvalp->ident = makeIdentifier(m_buffer16); + token = IDENT; + break; + case IdentifierOrKeyword: { + lvalp->ident = makeIdentifier(m_buffer16); + const HashEntry* entry = m_mainTable.entry(m_globalData, *lvalp->ident); + if (!entry) { + // Lookup for keyword failed, means this is an identifier. + token = IDENT; + break; + } + token = entry->lexerValue(); + // Hack for "f = function somename() { ... }"; too hard to get into the grammar. + m_eatNextIdentifier = token == FUNCTION && m_lastToken == '='; + if (token == CONTINUE || token == BREAK || token == RETURN || token == THROW) + m_restrKeyword = true; + break; + } + case String: + // Atomize constant strings in case they're later used in property lookup. + lvalp->ident = makeIdentifier(m_buffer16); + token = STRING; + break; + case Number: + lvalp->doubleValue = dval; + token = NUMBER; + break; + case Bad: #ifdef KJS_DEBUG_LEX - fprintf(stderr, "yylex: ERROR.\n"); + 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; + m_error = true; + return -1; + default: + ASSERT(!"unhandled numeration value in switch"); + m_error = true; + return -1; + } + m_lastToken = token; + return token; } bool Lexer::isWhiteSpace() const { - return current == '\t' || current == 0x0b || current == 0x0c || isSeparatorSpace(current); + return m_current == '\t' || m_current == 0x0b || m_current == 0x0c || isSeparatorSpace(m_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 cr = (m_current == '\r'); + bool lf = (m_current == '\n'); + if (cr) + m_skipLF = true; + else if (lf) + m_skipCR = true; + return cr || lf || m_current == 0x2028 || m_current == 0x2029; } bool Lexer::isIdentStart(int c) { - return (category(c) & (Letter_Uppercase | Letter_Lowercase | Letter_Titlecase | Letter_Modifier | Letter_Other)) - || c == '$' || 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 == '_'; + 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'); + return (c >= '0' && c <= '9'); } bool Lexer::isHexDigit(int c) { - return (c >= '0' && c <= '9' || - c >= 'a' && c <= 'f' || - c >= 'A' && c <= 'F'); + return (c >= '0' && c <= '9' + || c >= 'a' && c <= 'f' + || c >= 'A' && c <= 'F'); } bool Lexer::isOctalDigit(int c) { - return (c >= '0' && c <= '7'); + return (c >= '0' && c <= '7'); } -int Lexer::matchPunctuator(int c1, int c2, int c3, int c4) +int Lexer::matchPunctuator(int& charPos, 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; - } + if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { + shift(4); + return URSHIFTEQUAL; + } + if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return STREQ; + } + if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return STRNEQ; + } + if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return URSHIFT; + } + if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return LSHIFTEQUAL; + } + if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return RSHIFTEQUAL; + } + if (c1 == '<' && c2 == '=') { + shift(2); + return LE; + } + if (c1 == '>' && c2 == '=') { + shift(2); + return GE; + } + if (c1 == '!' && c2 == '=') { + shift(2); + return NE; + } + if (c1 == '+' && c2 == '+') { + shift(2); + if (m_terminator) + return AUTOPLUSPLUS; + return PLUSPLUS; + } + if (c1 == '-' && c2 == '-') { + shift(2); + if (m_terminator) + return AUTOMINUSMINUS; + return MINUSMINUS; + } + if (c1 == '=' && c2 == '=') { + shift(2); + return EQEQ; + } + if (c1 == '+' && c2 == '=') { + shift(2); + return PLUSEQUAL; + } + if (c1 == '-' && c2 == '=') { + shift(2); + return MINUSEQUAL; + } + if (c1 == '*' && c2 == '=') { + shift(2); + return MULTEQUAL; + } + if (c1 == '/' && c2 == '=') { + shift(2); + return DIVEQUAL; + } + if (c1 == '&' && c2 == '=') { + shift(2); + return ANDEQUAL; + } + if (c1 == '^' && c2 == '=') { + shift(2); + return XOREQUAL; + } + if (c1 == '%' && c2 == '=') { + shift(2); + return MODEQUAL; + } + if (c1 == '|' && c2 == '=') { + shift(2); + return OREQUAL; + } + if (c1 == '<' && c2 == '<') { + shift(2); + return LSHIFT; + } + if (c1 == '>' && c2 == '>') { + shift(2); + return RSHIFT; + } + if (c1 == '&' && c2 == '&') { + shift(2); + return AND; + } + 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 ';': + shift(1); + return static_cast<int>(c1); + case '{': + charPos = m_position - 4; + shift(1); + return OPENBRACE; + case '}': + charPos = m_position - 4; + shift(1); + return CLOSEBRACE; + 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; - } + 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'); + 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); + 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)); + return ((convertHex(c1) << 4) + convertHex(c2)); } -KJS::UChar Lexer::convertUnicode(int c1, int c2, int c3, int c4) +UChar Lexer::convertUnicode(int c1, int c2, int c3, int c4) { - return KJS::UChar((convertHex(c1) << 4) + convertHex(c2), - (convertHex(c3) << 4) + convertHex(c4)); + unsigned char highByte = (convertHex(c1) << 4) + convertHex(c2); + unsigned char lowByte = (convertHex(c3) << 4) + convertHex(c4); + return (highByte << 8 | lowByte); } void Lexer::record8(int c) @@ -812,48 +843,47 @@ void Lexer::record16(int c) record16(UChar(static_cast<unsigned short>(c))); } -void Lexer::record16(KJS::UChar c) +void Lexer::record16(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; + m_buffer16.clear(); + bool lastWasEscape = false; + bool inBrackets = false; + + while (1) { + if (isLineTerminator() || m_current == -1) + return false; + else if (m_current != '/' || lastWasEscape == true || inBrackets == true) { + // keep track of '[' and ']' + if (!lastWasEscape) { + if ( m_current == '[' && !inBrackets ) + inBrackets = true; + if ( m_current == ']' && inBrackets ) + inBrackets = false; + } + record16(m_current); + lastWasEscape = + !lastWasEscape && (m_current == '\\'); + } else { // end of regexp + m_pattern = UString(m_buffer16); + m_buffer16.clear(); + shift(1); + break; } - 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); + while (isIdentPart(m_current)) { + record16(m_current); + shift(1); + } + m_flags = UString(m_buffer16); - return true; + return true; } void Lexer::clear() @@ -864,7 +894,7 @@ void Lexer::clear() m_strings.swap(newStrings); deleteAllValues(m_identifiers); - Vector<KJS::Identifier*> newIdentifiers; + Vector<JSC::Identifier*> newIdentifiers; newIdentifiers.reserveCapacity(initialStringTableCapacity); m_identifiers.swap(newIdentifiers); @@ -880,18 +910,11 @@ void Lexer::clear() m_flags = 0; } -Identifier* Lexer::makeIdentifier(const Vector<KJS::UChar>& buffer) +Identifier* Lexer::makeIdentifier(const Vector<UChar>& buffer) { - KJS::Identifier* identifier = new KJS::Identifier(buffer.data(), buffer.size()); + JSC::Identifier* identifier = new JSC::Identifier(m_globalData, 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 +} // namespace JSC diff --git a/JavaScriptCore/kjs/lexer.h b/JavaScriptCore/kjs/lexer.h index 1ce27af..16bc4b6 100644 --- a/JavaScriptCore/kjs/lexer.h +++ b/JavaScriptCore/kjs/lexer.h @@ -1,4 +1,3 @@ -// -*- c-basic-offset: 2 -*- /* * This file is part of the KDE libraries * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) @@ -24,126 +23,142 @@ #ifndef Lexer_h #define Lexer_h +#include "lookup.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 +#include "SourceCode.h" + +namespace JSC { + + class Identifier; + class RegExp; + + class Lexer : Noncopyable { + public: + void setCode(const SourceCode&); + int lex(void* lvalp, void* llocp); + + int lineNo() const { return yylineno; } + + bool prevTerminator() const { return m_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 m_error; } + + void clear(); + SourceCode sourceCode(int openBrace, int closeBrace, int firstLine) { return SourceCode(m_source->provider(), m_source->startOffset() + openBrace + 1, m_source->startOffset() + closeBrace, firstLine); } + + private: + friend class JSGlobalData; + Lexer(JSGlobalData*); + ~Lexer(); + + void setDone(State); + void shift(unsigned int p); + void nextLine(); + int lookupKeyword(const char *); + + bool isWhiteSpace() const; + bool isLineTerminator(); + static bool isOctalDigit(int); + + int matchPunctuator(int& charPos, 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); + + JSC::Identifier* makeIdentifier(const Vector<UChar>& buffer); + + int yylineno; + int yycolumn; + + bool m_done; + Vector<char> m_buffer8; + Vector<UChar> m_buffer16; + bool m_terminator; + bool m_restrKeyword; + bool m_delimited; // encountered delimiter like "'" and "}" on last run + bool m_skipLF; + bool m_skipCR; + bool m_eatNextIdentifier; + int m_stackToken; + int m_lastToken; + + State m_state; + unsigned int m_position; + const SourceCode* m_source; + const UChar* m_code; + unsigned int m_length; + int m_atLineStart; + bool m_error; + + // current and following unicode characters (int to allow for -1 for end-of-file marker) + int m_current; + int m_next1; + int m_next2; + int m_next3; + + int m_currentOffset; + int m_nextOffset1; + int m_nextOffset2; + int m_nextOffset3; + + Vector<UString*> m_strings; + Vector<JSC::Identifier*> m_identifiers; + + JSGlobalData* m_globalData; + + UString m_pattern; + UString m_flags; + + const HashTable m_mainTable; + }; + +} // namespace JSC #endif // Lexer_h diff --git a/JavaScriptCore/kjs/list.cpp b/JavaScriptCore/kjs/list.cpp deleted file mode 100644 index 5cc7cc2..0000000 --- a/JavaScriptCore/kjs/list.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6a43e08..0000000 --- a/JavaScriptCore/kjs/list.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 index 95dc5cb..41ac725 100644 --- a/JavaScriptCore/kjs/lookup.cpp +++ b/JavaScriptCore/kjs/lookup.cpp @@ -1,7 +1,5 @@ -// -*- c-basic-offset: 2 -*- /* - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007 Apple Inc. All rights reserved. + * 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 @@ -22,60 +20,49 @@ #include "config.h" #include "lookup.h" -#include <wtf/Assertions.h> +#include "PrototypeFunction.h" -namespace KJS { +namespace JSC { -static inline bool keysMatch(const UChar* c, unsigned len, const char* s) +void HashTable::createTable(JSGlobalData* globalData) const { - // 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; + ASSERT(!table); + HashEntry* entries = new HashEntry[hashSizeMask + 1]; + for (int i = 0; i <= hashSizeMask; ++i) + entries[i].setKey(0); + for (int i = 0; values[i].key; ++i) { + UString::Rep* identifier = Identifier::add(globalData, values[i].key).releaseRef(); + int hashIndex = identifier->computedHash() & hashSizeMask; + ASSERT(!entries[hashIndex].key()); + entries[hashIndex].initialize(identifier, values[i].attributes, values[i].value1, values[i].value2); + } + table = entries; } -static inline const HashEntry* findEntry(const struct HashTable* table, unsigned int hash, - const UChar* c, unsigned int len) +void HashTable::deleteTable() const { - 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; + if (table) { + for (int i = 0; i != hashSizeMask + 1; ++i) { + if (UString::Rep* key = table[i].key()) + key->deref(); + } + delete [] table; + table = 0; + } } -const HashEntry* Lookup::findEntry(const struct HashTable* table, const Identifier& s) +void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) { - return KJS::findEntry(table, s.ustring().rep()->computedHash(), s.data(), s.size()); -} + ASSERT(entry->attributes() & Function); + JSValue** location = thisObj->getDirectLocation(propertyName); -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; -} + if (!location) { + PrototypeFunction* function = new (exec) PrototypeFunction(exec, entry->functionLength(), propertyName, entry->function()); + thisObj->putDirect(propertyName, function, entry->attributes()); + location = thisObj->getDirectLocation(propertyName); + } -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; + slot.setValueSlot(thisObj, location, thisObj->offsetForLocation(location)); } -} +} // namespace JSC diff --git a/JavaScriptCore/kjs/lookup.h b/JavaScriptCore/kjs/lookup.h index 199c9d9..a547613 100644 --- a/JavaScriptCore/kjs/lookup.h +++ b/JavaScriptCore/kjs/lookup.h @@ -1,7 +1,6 @@ -// -*- c-basic-offset: 2 -*- /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2003, 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 @@ -23,320 +22,217 @@ #define KJS_lookup_h #include "ExecState.h" -#include "function.h" -#include "identifier.h" +#include "JSFunction.h" #include "JSGlobalObject.h" -#include "object.h" +#include "JSObject.h" +#include "PropertySlot.h" +#include "identifier.h" #include <stdio.h> #include <wtf/Assertions.h> -namespace KJS { +namespace JSC { - /** - * An entry in a hash table. - */ - struct HashEntry { - /** - * s is the key (e.g. a property name) - */ - const char* s; + // Hash table generated by the create_hash_table script. + struct HashTableValue { + const char* key; // property name + unsigned char attributes; // JSObject attributes + intptr_t value1; + intptr_t value2; + }; - /** - * 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; + // FIXME: There is no reason this get function can't be simpler. + // ie. typedef JSValue* (*GetFunction)(ExecState*, JSObject* baseObject) + typedef PropertySlot::GetValueFunc GetFunction; + typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue* value); + + class HashEntry { + public: + void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2) + { + m_key = key; + m_attributes = attributes; + m_u.store.value1 = v1; + m_u.store.value2 = v2; + } + + void setKey(UString::Rep* key) { m_key = key; } + UString::Rep* key() const { return m_key; } + + unsigned char attributes() const { return m_attributes; } + + NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; } + unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); } + + GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; } + PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; } + + intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; } + + private: + UString::Rep* m_key; + unsigned char m_attributes; // JSObject attributes + + union { + struct { + intptr_t value1; + intptr_t value2; + } store; + struct { + NativeFunction functionValue; + intptr_t length; // number of arguments for function + } function; + struct { + GetFunction get; + PutFunction put; + } property; + struct { + intptr_t value; + intptr_t unused; + } lexer; + } m_u; + }; + + struct HashTable { + int hashSizeMask; // Precomputed size for the hash table (minus 1). + const HashTableValue* values; // Fixed values generated by script. + mutable const HashEntry* table; // Table allocated at runtime. + + ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const + { + if (!table) + createTable(globalData); + } + + ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const + { + if (!table) + createTable(&exec->globalData()); + } + + void deleteTable() const; + + // Find an entry in the table, and return the entry. + ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const + { + initializeIfNeeded(globalData); + return entry(identifier); + } + + ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const + { + initializeIfNeeded(exec); + return entry(identifier); + } + + private: + ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const + { + ASSERT(table); + const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & hashSizeMask]; + if (entry->key() != identifier.ustring().rep()) + return 0; + return entry; + } + + // Convert the hash table keys to identifiers. + void createTable(JSGlobalData*) const; + }; + + void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&); /** - * 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 + * 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). */ - int size; + template <class ThisImp, class ParentImp> + inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + if (entry->attributes() & Function) + setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + else + slot.setCustom(thisObj, entry->propertyGetter()); + + return true; + } + /** - * pointer to the array of entries - * Mind that some entries in the array are null (0,0,0,0). + * Simplified version of getStaticPropertySlot in case there are only functions. + * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing + * a dummy getValueProperty. */ - const HashEntry* entries; + template <class ParentImp> + inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot)) + return true; + + const HashEntry* entry = table->entry(exec, propertyName); + if (!entry) + return false; + + setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + return true; + } + /** - * the maximum value for the hash minus 1. Always smaller than size. + * Simplified version of getStaticPropertySlot in case there are no functions, only "values". + * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. */ - int hashSizeMask; - }; - - /** - * @short Fast keyword lookup. - */ - class Lookup { - public: + template <class ThisImp, class ParentImp> + inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + + ASSERT(!(entry->attributes() & Function)); + + slot.setCustom(thisObj, entry->propertyGetter()); + return true; + } + /** - * Find an entry in the table, and return its value (i.e. the value field of HashEntry) + * 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. */ - static int find(const struct HashTable*, const Identifier&); - static int find(const struct HashTable*, const UChar*, unsigned int len); + template <class ThisImp> + inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue* value, const HashTable* table, ThisImp* thisObj) + { + const HashEntry* entry = table->entry(exec, propertyName); + + if (!entry) + return false; + + if (entry->attributes() & Function) // function: put as override property + thisObj->putDirect(propertyName, value); + else if (!(entry->attributes() & ReadOnly)) + entry->propertyPutter()(exec, thisObj, value); + + return true; + } /** - * 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. + * 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. */ - 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, int attr, - 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->JSObject::put(exec, propertyName, value, attr); - else if (!(entry->attr & ReadOnly)) - thisObj->putValueProperty(exec, entry->value.intValue, value, attr); - - 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, int attr, - const HashTable* table, ThisImp* thisObj) - { - if (!lookupPut<ThisImp>(exec, propertyName, value, attr, table, thisObj)) - thisObj->ParentImp::put(exec, propertyName, value, attr); // 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); + template <class ThisImp, class ParentImp> + inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue* value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot) + { + if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj)) + thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent } - JSObject* newObject = new ClassCtor(exec); - globalObject->putDirect(propertyName, newObject, Internal | 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); \ - } +} // namespace JSC #endif // KJS_lookup_h diff --git a/JavaScriptCore/kjs/math_object.cpp b/JavaScriptCore/kjs/math_object.cpp deleted file mode 100644 index 805efda..0000000 --- a/JavaScriptCore/kjs/math_object.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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 deleted file mode 100644 index 72e442f..0000000 --- a/JavaScriptCore/kjs/math_object.h +++ /dev/null @@ -1,65 +0,0 @@ -// -*- 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 index 94ef47d..b4aeb28 100644 --- a/JavaScriptCore/kjs/nodes.cpp +++ b/JavaScriptCore/kjs/nodes.cpp @@ -26,235 +26,127 @@ #include "config.h" #include "nodes.h" +#include "CodeGenerator.h" #include "ExecState.h" #include "JSGlobalObject.h" +#include "JSStaticScopeObject.h" +#include "LabelScope.h" #include "Parser.h" #include "PropertyNameArray.h" -#include "array_object.h" -#include "debugger.h" -#include "function_object.h" +#include "RegExpObject.h" +#include "SamplingTool.h" +#include "Debugger.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> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/Threading.h> -namespace KJS { +using namespace WTF; -class FunctionBodyNodeWithDebuggerHooks : public FunctionBodyNode { -public: - FunctionBodyNodeWithDebuggerHooks(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; -}; - -#define KJS_CHECKEXCEPTION \ -if (exec->hadException()) \ - return rethrowException(exec); - -#define KJS_CHECKEXCEPTIONVALUE \ -if (exec->hadException()) { \ - handleException(exec); \ - return jsUndefined(); \ -} - -#define KJS_CHECKEXCEPTIONNUMBER \ -if (exec->hadException()) { \ - handleException(exec); \ - return 0; \ -} - -#define KJS_CHECKEXCEPTIONBOOLEAN \ -if (exec->hadException()) { \ - handleException(exec); \ - return false; \ -} - -#define KJS_CHECKEXCEPTIONVOID \ -if (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; -} +namespace JSC { // ------------------------------ Node ----------------------------------------- #ifndef NDEBUG -#ifndef LOG_CHANNEL_PREFIX -#define LOG_CHANNEL_PREFIX Log +static RefCountedLeakCounter parserRefCountedCounter("JSC::Node"); #endif -static WTFLogChannel LogKJSNodeLeaks = { 0x00000000, "", WTFLogChannelOn }; - -struct ParserRefCountedCounter { - static unsigned count; - ParserRefCountedCounter() - { - if (count) -#ifdef ANDROID // FIXME HACK : LOG not defined appropriately here - ASSERT("LEAK: KJS::Node"); -#else - LOG(KJSNodeLeaks, "LEAK: %u KJS::Node\n", count); -#endif - } -}; -unsigned ParserRefCountedCounter::count = 0; -static ParserRefCountedCounter parserRefCountedCounter; -#endif - -static HashSet<ParserRefCounted*>* newTrackedObjects; -static HashCountedSet<ParserRefCounted*>* trackedObjectExtraRefCounts; -ParserRefCounted::ParserRefCounted() +ParserRefCounted::ParserRefCounted(JSGlobalData* globalData) + : m_globalData(globalData) { #ifndef NDEBUG - ++ParserRefCountedCounter::count; + parserRefCountedCounter.increment(); #endif - if (!newTrackedObjects) - newTrackedObjects = new HashSet<ParserRefCounted*>; - newTrackedObjects->add(this); - ASSERT(newTrackedObjects->contains(this)); + if (!m_globalData->newParserObjects) + m_globalData->newParserObjects = new HashSet<ParserRefCounted*>; + m_globalData->newParserObjects->add(this); + ASSERT(m_globalData->newParserObjects->contains(this)); } ParserRefCounted::~ParserRefCounted() { #ifndef NDEBUG - --ParserRefCountedCounter::count; + parserRefCountedCounter.decrement(); #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)); + if (m_globalData->newParserObjects) { + HashSet<ParserRefCounted*>::iterator it = m_globalData->newParserObjects->find(this); + if (it != m_globalData->newParserObjects->end()) { + m_globalData->newParserObjects->remove(it); + ASSERT(!m_globalData->parserObjectExtraRefCounts || !m_globalData->parserObjectExtraRefCounts->contains(this)); return; } } - ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); + ASSERT(!m_globalData->newParserObjects || !m_globalData->newParserObjects->contains(this)); - if (!trackedObjectExtraRefCounts) - trackedObjectExtraRefCounts = new HashCountedSet<ParserRefCounted*>; - trackedObjectExtraRefCounts->add(this); + if (!m_globalData->parserObjectExtraRefCounts) + m_globalData->parserObjectExtraRefCounts = new HashCountedSet<ParserRefCounted*>; + m_globalData->parserObjectExtraRefCounts->add(this); } void ParserRefCounted::deref() { - ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); + ASSERT(!m_globalData->newParserObjects || !m_globalData->newParserObjects->contains(this)); - if (!trackedObjectExtraRefCounts) { + if (!m_globalData->parserObjectExtraRefCounts) { delete this; return; } - HashCountedSet<ParserRefCounted*>::iterator it = trackedObjectExtraRefCounts->find(this); - if (it == trackedObjectExtraRefCounts->end()) + HashCountedSet<ParserRefCounted*>::iterator it = m_globalData->parserObjectExtraRefCounts->find(this); + if (it == m_globalData->parserObjectExtraRefCounts->end()) delete this; else - trackedObjectExtraRefCounts->remove(it); + m_globalData->parserObjectExtraRefCounts->remove(it); } -unsigned ParserRefCounted::refcount() +bool ParserRefCounted::hasOneRef() { - if (newTrackedObjects && newTrackedObjects->contains(this)) { - ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this)); - return 0; + if (m_globalData->newParserObjects && m_globalData->newParserObjects->contains(this)) { + ASSERT(!m_globalData->parserObjectExtraRefCounts || !m_globalData->parserObjectExtraRefCounts->contains(this)); + return false; } - ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); + ASSERT(!m_globalData->newParserObjects || !m_globalData->newParserObjects->contains(this)); - if (!trackedObjectExtraRefCounts) - return 1; + if (!m_globalData->parserObjectExtraRefCounts) + return true; - return 1 + trackedObjectExtraRefCounts->count(this); + return !m_globalData->parserObjectExtraRefCounts->contains(this); } -void ParserRefCounted::deleteNewObjects() +void ParserRefCounted::deleteNewObjects(JSGlobalData* globalData) { - if (!newTrackedObjects) + if (!globalData->newParserObjects) return; #ifndef NDEBUG - HashSet<ParserRefCounted*>::iterator end = newTrackedObjects->end(); - for (HashSet<ParserRefCounted*>::iterator it = newTrackedObjects->begin(); it != end; ++it) - ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(*it)); + HashSet<ParserRefCounted*>::iterator end = globalData->newParserObjects->end(); + for (HashSet<ParserRefCounted*>::iterator it = globalData->newParserObjects->begin(); it != end; ++it) + ASSERT(!globalData->parserObjectExtraRefCounts || !globalData->parserObjectExtraRefCounts->contains(*it)); #endif - deleteAllValues(*newTrackedObjects); - delete newTrackedObjects; - newTrackedObjects = 0; -} - -Node::Node() - : m_expectedReturnType(ObjectType) -{ - m_line = lexer().lineNo(); + deleteAllValues(*globalData->newParserObjects); + delete globalData->newParserObjects; + globalData->newParserObjects = 0; } -Node::Node(JSType expectedReturn) - : m_expectedReturnType(expectedReturn) +Node::Node(JSGlobalData* globalData) + : ParserRefCounted(globalData) { - m_line = lexer().lineNo(); + m_line = globalData->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) JSC_FAST_CALL; static void substitute(UString& string, const UString& substring) { int position = string.find("%s"); @@ -265,124 +157,30 @@ static void substitute(UString& string, const UString& substring) 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) +RegisterID* ThrowableExpressionData::emitThrowError(CodeGenerator& generator, ErrorType e, const char* msg) { - 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)); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RegisterID* exception = generator.emitNewError(generator.newTemporary(), e, jsString(generator.globalData(), msg)); + generator.emitThrow(exception); + return exception; } -JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* expr, const Identifier& label) +RegisterID* ThrowableExpressionData::emitThrowError(CodeGenerator& generator, ErrorType e, const char* msg, 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)) { - bool cont = dbg->exception(exec, currentSourceId(exec), m_line, exceptionValue); - if (!cont) - dbg->imp()->abort(); - } + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RegisterID* exception = generator.emitNewError(generator.newTemporary(), e, jsString(generator.globalData(), message)); + generator.emitThrow(exception); + return exception; } - -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) +StatementNode::StatementNode(JSGlobalData* globalData) + : Node(globalData) + , m_lastLine(-1) { - m_line = -1; } void StatementNode::setLoc(int firstLine, int lastLine) @@ -398,3535 +196,1077 @@ 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()); + m_statements.append(statement); } // ------------------------------ NullNode ------------------------------------- -JSValue* NullNode::evaluate(ExecState* ) -{ - return jsNull(); -} - -// ------------------------------ FalseNode ---------------------------------- - -JSValue* FalseNode::evaluate(ExecState*) +RegisterID* NullNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return jsBoolean(false); + if (dst == ignoredResult()) + return 0; + return generator.emitLoad(dst, jsNull()); } -// ------------------------------ TrueNode ---------------------------------- +// ------------------------------ BooleanNode ---------------------------------- -JSValue* TrueNode::evaluate(ExecState*) +RegisterID* BooleanNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return jsBoolean(true); + if (dst == ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); } // ------------------------------ 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*) +RegisterID* NumberNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - uint32_t i; - if (JSImmediate::getTruncatedUInt32(m_value, i)) - return i; - bool ok; - return JSValue::toUInt32SlowCase(m_double, ok); + if (dst == ignoredResult()) + return 0; + return generator.emitLoad(dst, m_double); } // ------------------------------ StringNode ----------------------------------- -JSValue* StringNode::evaluate(ExecState*) -{ - return jsOwnedString(m_value); -} - -double StringNode::evaluateToNumber(ExecState*) -{ - return m_value.toDouble(); -} - -bool StringNode::evaluateToBoolean(ExecState*) +RegisterID* StringNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return !m_value.isEmpty(); + if (dst == ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); } // ------------------------------ RegExpNode ----------------------------------- -JSValue* RegExpNode::evaluate(ExecState* exec) +RegisterID* RegExpNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return exec->lexicalGlobalObject()->regExpConstructor()->createRegExpImp(exec, m_regExp); + RefPtr<RegExp> regExp = RegExp::create(generator.globalData(), m_pattern, m_flags); + if (!regExp->isValid()) + return emitThrowError(generator, SyntaxError, ("Invalid regular expression: " + UString(regExp->errorMessage())).UTF8String().c_str()); + if (dst == ignoredResult()) + return 0; + return generator.emitNewRegExp(generator.finalDestination(dst), regExp.get()); } // ------------------------------ ThisNode ------------------------------------- -// ECMA 11.1.1 -JSValue* ThisNode::evaluate(ExecState* exec) +RegisterID* ThisNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return exec->thisValue(); + if (dst == ignoredResult()) + return 0; + return generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); } // ------------------------------ 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) +bool ResolveNode::isPure(CodeGenerator& generator) const { - JSValue* v = inlineEvaluate(exec); - KJS_CHECKEXCEPTIONNUMBER - return v->toNumber(exec); + return generator.isLocal(m_ident); } -bool ResolveNode::evaluateToBoolean(ExecState* exec) +RegisterID* ResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + if (RegisterID* local = generator.registerFor(m_ident)) { + if (dst == ignoredResult()) + return 0; + return generator.moveToDestinationIfNeeded(dst, local); + } + + generator.emitExpressionInfo(m_startOffset + m_ident.size(), m_ident.size(), 0); + return generator.emitResolve(generator.finalDestination(dst), m_ident); } -// ------------------------------ ElementNode ---------------------------------- +// ------------------------------ ArrayNode ------------------------------------ -void ElementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ArrayNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - if (m_next) - nodeStack.append(m_next.get()); - ASSERT(m_node); - nodeStack.append(m_node.get()); -} + // FIXME: Should we put all of this code into emitNewArray? -// 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); + unsigned length = 0; + ElementNode* firstPutElement; + for (firstPutElement = m_element.get(); firstPutElement; firstPutElement = firstPutElement->next()) { + if (firstPutElement->elision()) + break; + ++length; } - return array; -} -// ------------------------------ ArrayNode ------------------------------------ + if (!firstPutElement && !m_elision) + return generator.emitNewArray(generator.finalDestination(dst), m_element.get()); -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; + RefPtr<RegisterID> array = generator.emitNewArray(generator.tempDestination(dst), m_element.get()); - 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; + for (ElementNode* n = firstPutElement; n; n = n->next()) { + RegisterID* value = generator.emitNode(n->value()); + length += n->elision(); + generator.emitPutByIndex(array.get(), length++, value); } - if (m_optional) - array->put(exec, exec->propertyNames().length, jsNumber(m_elision + length)); + if (m_elision) { + RegisterID* value = generator.emitLoad(0, jsNumber(generator.globalData(), m_elision + length)); + generator.emitPutById(array.get(), generator.propertyNames().length, value); + } - return array; + return generator.moveToDestinationIfNeeded(dst, array.get()); } // ------------------------------ ObjectLiteralNode ---------------------------- -void ObjectLiteralNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ObjectLiteralNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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()); + if (!m_list) { + if (dst == ignoredResult()) + return 0; + return generator.emitNewObject(generator.finalDestination(dst)); + } + return generator.emitNode(dst, m_list.get()); } // ------------------------------ 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) +RegisterID* PropertyListNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSObject* obj = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); - + RefPtr<RegisterID> newObj = generator.tempDestination(dst); + + generator.emitNewObject(newObj.get()); + for (PropertyListNode* p = this; p; p = p->m_next.get()) { - JSValue* v = p->m_node->m_assign->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - + RegisterID* value = generator.emitNode(p->m_node->m_assign.get()); + switch (p->m_node->m_type) { - case PropertyNode::Getter: - ASSERT(v->isObject()); - obj->defineGetter(exec, p->m_node->name(), static_cast<JSObject* >(v)); + case PropertyNode::Constant: { + generator.emitPutById(newObj.get(), p->m_node->name(), value); break; - case PropertyNode::Setter: - ASSERT(v->isObject()); - obj->defineSetter(exec, p->m_node->name(), static_cast<JSObject* >(v)); + } + case PropertyNode::Getter: { + generator.emitPutGetter(newObj.get(), p->m_node->name(), value); break; - case PropertyNode::Constant: - obj->put(exec, p->m_node->name(), v); + } + case PropertyNode::Setter: { + generator.emitPutSetter(newObj.get(), p->m_node->name(), value); break; + } + default: + ASSERT_NOT_REACHED(); } } - - 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(); + + return generator.moveToDestinationIfNeeded(dst, newObj.get()); } // ------------------------------ 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) +RegisterID* BracketAccessorNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base.get(), m_subscriptHasAssignments, m_subscript->isPure(generator)); + RegisterID* property = generator.emitNode(m_subscript.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitGetByVal(generator.finalDestination(dst), base.get(), property); } // ------------------------------ 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) +RegisterID* DotAccessorNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v = inlineEvaluate(exec); - KJS_CHECKEXCEPTIONNUMBER - return v->toUInt32(exec); + RegisterID* base = generator.emitNode(m_base.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitGetById(generator.finalDestination(dst), base, m_ident); } // ------------------------------ ArgumentListNode ----------------------------- -void ArgumentListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ArgumentListNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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()); + return generator.emitNode(dst, m_expr.get()); } // ------------------------------ NewExprNode ---------------------------------- -void NewExprNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* NewExprNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - if (m_args) - nodeStack.append(m_args.get()); - nodeStack.append(m_expr.get()); + RefPtr<RegisterID> func = generator.emitNode(m_expr.get()); + return generator.emitConstruct(generator.finalDestination(dst), func.get(), m_args.get(), m_divot, m_startOffset, m_endOffset); } -// ECMA 11.2.2 - -JSValue* NewExprNode::inlineEvaluate(ExecState* exec) +RegisterID* EvalFunctionCallNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + RefPtr<RegisterID> base = generator.tempDestination(dst); + RefPtr<RegisterID> func = generator.newTemporary(); + generator.emitResolveWithBase(base.get(), func.get(), generator.propertyNames().eval); + return generator.emitCallEval(generator.finalDestination(dst, base.get()), func.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset); } -JSValue* NewExprNode::evaluate(ExecState* exec) +RegisterID* FunctionCallValueNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return inlineEvaluate(exec); + RefPtr<RegisterID> func = generator.emitNode(m_expr.get()); + return generator.emitCall(generator.finalDestination(dst), func.get(), 0, m_args.get(), m_divot, m_startOffset, m_endOffset); } -double NewExprNode::evaluateToNumber(ExecState* exec) +RegisterID* FunctionCallResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v = inlineEvaluate(exec); - KJS_CHECKEXCEPTIONNUMBER - return v->toNumber(exec); -} + if (RefPtr<RegisterID> local = generator.registerFor(m_ident)) + return generator.emitCall(generator.finalDestination(dst), local.get(), 0, m_args.get(), m_divot, m_startOffset, m_endOffset); -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()); + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); + return generator.emitCall(generator.finalDestination(dst), func.get(), 0, m_args.get(), m_divot, m_startOffset, m_endOffset); } - 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); + RefPtr<RegisterID> base = generator.tempDestination(dst); + RefPtr<RegisterID> func = generator.newTemporary(); + int identifierStart = m_divot - m_startOffset; + generator.emitExpressionInfo(identifierStart + m_ident.size(), m_ident.size(), 0); + generator.emitResolveFunction(base.get(), func.get(), m_ident); + return generator.emitCall(generator.finalDestination(dst, base.get()), func.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset); } -int32_t FunctionCallDotNode::evaluateToInt32(ExecState* exec) +RegisterID* FunctionCallBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v = inlineEvaluate(exec); - KJS_CHECKEXCEPTIONNUMBER - return v->toInt32(exec); + RefPtr<RegisterID> base = generator.emitNode(m_base.get()); + RegisterID* property = generator.emitNode(m_subscript.get()); + generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetByVal(generator.newTemporary(), base.get(), property); + return generator.emitCall(generator.finalDestination(dst, base.get()), function.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset); } -uint32_t FunctionCallDotNode::evaluateToUInt32(ExecState* exec) +RegisterID* FunctionCallDotNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v = inlineEvaluate(exec); - KJS_CHECKEXCEPTIONNUMBER - return v->toUInt32(exec); + RefPtr<RegisterID> base = generator.emitNode(m_base.get()); + generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetById(generator.newTemporary(), base.get(), m_ident); + return generator.emitCall(generator.finalDestination(dst, base.get()), function.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset); } -// 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) +static RegisterID* emitPreIncOrDec(CodeGenerator& generator, RegisterID* srcDst, Operator oper) { - // 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); + return (oper == OpPlusPlus) ? generator.emitPreInc(srcDst) : generator.emitPreDec(srcDst); } -void PostIncResolveNode::optimizeForUnnecessaryResult() +static RegisterID* emitPostIncOrDec(CodeGenerator& generator, RegisterID* dst, RegisterID* srcDst, Operator oper) { - new (this) PreIncResolveNode(PlacementNewAdopt); + return (oper == OpPlusPlus) ? generator.emitPostInc(dst, srcDst) : generator.emitPostDec(dst, srcDst); } -JSValue* PostIncLocalVarNode::evaluate(ExecState* exec) +RegisterID* PostfixResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); -} + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + if (dst == ignoredResult()) + return 0; + return generator.emitToJSNumber(generator.finalDestination(dst), local); + } -// 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); + if (dst == ignoredResult()) + return emitPreIncOrDec(generator, local, m_operator); + return emitPostIncOrDec(generator, generator.finalDestination(dst), local, m_operator); } -} - -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; + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> value = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); + RegisterID* oldValue; + if (dst == ignoredResult()) { + oldValue = 0; + emitPreIncOrDec(generator, value.get(), m_operator); + } else { + oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); } + generator.emitPutScopedVar(depth, index, value.get(), globalObject); + return oldValue; + } - ++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); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RefPtr<RegisterID> value = generator.newTemporary(); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), value.get(), m_ident); + RegisterID* oldValue; + if (dst == ignoredResult()) { + oldValue = 0; + emitPreIncOrDec(generator, value.get(), m_operator); + } else { + oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); + } + generator.emitPutById(base.get(), m_ident, value.get()); + return oldValue; } // ------------------------------ 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) +RegisterID* PostfixBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* baseValue = m_base->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - JSValue* subscript = m_subscript->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - - JSObject* base = baseValue->toObject(exec); + RefPtr<RegisterID> base = generator.emitNode(m_base.get()); + RefPtr<RegisterID> property = generator.emitNode(m_subscript.get()); - 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; + generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get()); + RegisterID* oldValue; + if (dst == ignoredResult()) { + oldValue = 0; + if (m_operator == OpPlusPlus) + generator.emitPreInc(value.get()); + else + generator.emitPreDec(value.get()); + } else { + oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); } - - 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; + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutByVal(base.get(), property.get(), value.get()); + return oldValue; } // ------------------------------ 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) +RegisterID* PostfixDotNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* baseValue = m_base->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - JSObject* base = baseValue->toObject(exec); + RefPtr<RegisterID> base = generator.emitNode(m_base.get()); - 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; + generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetById(generator.newTemporary(), base.get(), m_ident); + RegisterID* oldValue; + if (dst == ignoredResult()) { + oldValue = 0; + if (m_operator == OpPlusPlus) + generator.emitPreInc(value.get()); + else + generator.emitPreDec(value.get()); + } else { + oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); + } + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutById(base.get(), m_ident, value.get()); + return oldValue; } // ------------------------------ PostfixErrorNode ----------------------------------- -JSValue* PostfixErrorNode::evaluate(ExecState* exec) +RegisterID* PostfixErrorNode::emitCode(CodeGenerator& generator, RegisterID*) { - throwError(exec, ReferenceError, "Postfix %s operator applied to value that is not a reference.", - m_operator == OpPlusPlus ? "++" : "--"); - handleException(exec); - return jsUndefined(); + return emitThrowError(generator, ReferenceError, m_operator == OpPlusPlus ? "Postfix ++ operator applied to value that is not a reference." : "Postfix -- operator applied to value that is not a reference."); } // ------------------------------ 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) +RegisterID* DeleteResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - // 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); -} + if (generator.registerFor(m_ident)) + return generator.emitUnexpectedLoad(generator.finalDestination(dst), false); -JSValue* LocalVarDeleteNode::evaluate(ExecState*) -{ - return jsBoolean(false); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RegisterID* base = generator.emitResolveBase(generator.tempDestination(dst), m_ident); + return generator.emitDeleteById(generator.finalDestination(dst, base), base, m_ident); } // ------------------------------ 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) +RegisterID* DeleteBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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)); + RefPtr<RegisterID> r0 = generator.emitNode(m_base.get()); + RegisterID* r1 = generator.emitNode(m_subscript.get()); - Identifier propertyName(subscript->toString(exec)); - return jsBoolean(base->deleteProperty(exec, propertyName)); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1); } // ------------------------------ DeleteDotNode ----------------------------------- -void DeleteDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* DeleteDotNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_base.get()); -} - -JSValue* DeleteDotNode::evaluate(ExecState* exec) -{ - JSValue* baseValue = m_base->evaluate(exec); - JSObject* base = baseValue->toObject(exec); - KJS_CHECKEXCEPTIONVALUE + RegisterID* r0 = generator.emitNode(m_base.get()); - return jsBoolean(base->deleteProperty(exec, m_ident)); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitDeleteById(generator.finalDestination(dst), r0, m_ident); } // ------------------------------ DeleteValueNode ----------------------------------- -void DeleteValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) -{ - nodeStack.append(m_expr.get()); -} - -JSValue* DeleteValueNode::evaluate(ExecState* exec) +RegisterID* DeleteValueNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - m_expr->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE + generator.emitNode(ignoredResult(), m_expr.get()); // delete on a non-location expression ignores the value and returns true - return jsBoolean(true); + return generator.emitUnexpectedLoad(generator.finalDestination(dst), true); } // ------------------------------ VoidNode ------------------------------------- -void VoidNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* VoidNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_expr.get()); -} - -// ECMA 11.4.2 -JSValue* VoidNode::evaluate(ExecState* exec) -{ - m_expr->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - - return jsUndefined(); + if (dst == ignoredResult()) { + generator.emitNode(ignoredResult(), m_expr.get()); + return 0; + } + RefPtr<RegisterID> r0 = generator.emitNode(m_expr.get()); + return generator.emitLoad(dst, jsUndefined()); } -// ECMA 11.4.3 - // ------------------------------ TypeOfValueNode ----------------------------------- -void TypeOfValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* TypeOfResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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"); + if (RegisterID* local = generator.registerFor(m_ident)) { + if (dst == ignoredResult()) + return 0; + return generator.emitTypeOf(generator.finalDestination(dst), local); } -} - -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"); + RefPtr<RegisterID> scratch = generator.emitResolveBase(generator.tempDestination(dst), m_ident); + generator.emitGetById(scratch.get(), scratch.get(), m_ident); + if (dst == ignoredResult()) + return 0; + return generator.emitTypeOf(generator.finalDestination(dst, scratch.get()), scratch.get()); } // ------------------------------ TypeOfValueNode ----------------------------------- -JSValue* TypeOfValueNode::evaluate(ExecState* exec) +RegisterID* TypeOfValueNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + if (dst == ignoredResult()) { + generator.emitNode(ignoredResult(), m_expr.get()); + return 0; } + RefPtr<RegisterID> src = generator.emitNode(m_expr.get()); + return generator.emitTypeOf(generator.finalDestination(dst), src.get()); } -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; -} +// ------------------------------ PrefixResolveNode ---------------------------------- -JSValue* PreIncResolveNode::evaluate(ExecState* exec) +RegisterID* PrefixResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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; + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + if (dst == ignoredResult()) + return 0; + RefPtr<RegisterID> r0 = generator.emitUnexpectedLoad(generator.finalDestination(dst), (m_operator == OpPlusPlus) ? 1.0 : -1.0); + return generator.emitBinaryOp(op_add, r0.get(), local, r0.get(), OperandTypes()); } - ++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); + emitPreIncOrDec(generator, local, m_operator); + return generator.moveToDestinationIfNeeded(dst, local); } -} - -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 ---------------------------------- + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> propDst = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); + emitPreIncOrDec(generator, propDst.get(), m_operator); + generator.emitPutScopedVar(depth, index, propDst.get(), globalObject); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); + } -JSValue* PostDecConstNode::evaluate(ExecState* exec) -{ - ASSERT(exec->variableObject() == exec->scopeChain().top()); - return jsNumber(exec->localStorage()[m_index].value->toNumber(exec)); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), propDst.get(), m_ident); + emitPreIncOrDec(generator, propDst.get(), m_operator); + generator.emitPutById(base.get(), m_ident, propDst.get()); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); } // ------------------------------ 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) +RegisterID* PrefixBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + RefPtr<RegisterID> base = generator.emitNode(m_base.get()); + RefPtr<RegisterID> property = generator.emitNode(m_subscript.get()); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); - 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; + generator.emitExpressionInfo(m_divot + m_subexpressionDivotOffset, m_subexpressionStartOffset, m_endOffset - m_subexpressionDivotOffset); + RegisterID* value = generator.emitGetByVal(propDst.get(), base.get(), property.get()); + if (m_operator == OpPlusPlus) + generator.emitPreInc(value); + else + generator.emitPreDec(value); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutByVal(base.get(), property.get(), value); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); } // ------------------------------ 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) +RegisterID* PrefixDotNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + RefPtr<RegisterID> base = generator.emitNode(m_base.get()); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); - return n2; + generator.emitExpressionInfo(m_divot + m_subexpressionDivotOffset, m_subexpressionStartOffset, m_endOffset - m_subexpressionDivotOffset); + RegisterID* value = generator.emitGetById(propDst.get(), base.get(), m_ident); + if (m_operator == OpPlusPlus) + generator.emitPreInc(value); + else + generator.emitPreDec(value); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutById(base.get(), m_ident, value); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); } // ------------------------------ 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) +RegisterID* PrefixErrorNode::emitCode(CodeGenerator& generator, RegisterID*) { - return m_expr->evaluateToNumber(exec); + return emitThrowError(generator, ReferenceError, m_operator == OpPlusPlus ? "Prefix ++ operator applied to value that is not a reference." : "Prefix -- operator applied to value that is not a reference."); } -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)); -} +// ------------------------------ Unary Operation Nodes ----------------------------------- -double NegateNode::evaluateToNumber(ExecState* exec) +RegisterID* UnaryOpNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - // No need to check exception, caller will do so right after evaluateToNumber() - return -m_expr->evaluateToNumber(exec); + RegisterID* src = generator.emitNode(m_expr.get()); + return generator.emitUnaryOp(opcode(), generator.finalDestination(dst), src, m_expr->resultDescriptor()); } -// ------------------------------ BitwiseNotNode ------------------------------- +// ------------------------------ Binary Operation Nodes ----------------------------------- -void BitwiseNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* BinaryOpNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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; + OpcodeID opcode = this->opcode(); + if (opcode == op_neq) { + if (m_expr1->isNull() || m_expr2->isNull()) { + RefPtr<RegisterID> src = generator.emitNode(dst, m_expr1->isNull() ? m_expr2.get() : m_expr1.get()); + return generator.emitUnaryOp(op_neq_null, generator.finalDestination(dst, src.get()), src.get(), ResultType::unknown()); + } } - 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()); + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2.get()); + return generator.emitBinaryOp(opcode, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); } -// ECMA 11.8.7 -JSValue* InNode::evaluate(ExecState* exec) +RegisterID* EqualNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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; + if (m_expr1->isNull() || m_expr2->isNull()) { + RefPtr<RegisterID> src = generator.emitNode(dst, m_expr1->isNull() ? m_expr2.get() : m_expr1.get()); + return generator.emitUnaryOp(op_eq_null, generator.finalDestination(dst, src.get()), src.get(), ResultType::unknown()); } - 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); + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2.get()); + return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2); } -JSValue* EqualNode::evaluate(ExecState* exec) +RegisterID* StrictEqualNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return jsBoolean(inlineEvaluateToBoolean(exec)); + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2.get()); + return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2); } -bool EqualNode::evaluateToBoolean(ExecState* exec) +RegisterID* ReverseBinaryOpNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return inlineEvaluateToBoolean(exec); + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2.get()); + return generator.emitBinaryOp(opcode(), generator.finalDestination(dst, src1.get()), src2, src1.get(), OperandTypes(m_expr2->resultDescriptor(), m_expr1->resultDescriptor())); } -void NotEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ThrowableBinaryOpNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_expr2.get()); - nodeStack.append(m_expr1.get()); + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitBinaryOp(opcode(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); } -// ECMA 11.9.2 -bool NotEqualNode::inlineEvaluateToBoolean(ExecState* exec) +RegisterID* InstanceOfNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v1 = m_expr1->evaluate(exec); - KJS_CHECKEXCEPTIONBOOLEAN - JSValue* v2 = m_expr2->evaluate(exec); - KJS_CHECKEXCEPTIONBOOLEAN + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator)); + RefPtr<RegisterID> src2 = generator.emitNode(m_expr2.get()); - 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 + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RegisterID* src2Prototype = generator.emitGetById(generator.newTemporary(), src2.get(), generator.globalData()->propertyNames->prototype); - 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); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitInstanceOf(generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), src2Prototype); } // ------------------------------ 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) +RegisterID* LogicalOpNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v1 = m_expr1->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - if (v1->toBoolean(exec)) - return v1; - return m_expr2->evaluate(exec); -} + RefPtr<RegisterID> temp = generator.tempDestination(dst); + RefPtr<LabelID> target = generator.newLabel(); + + generator.emitNode(temp.get(), m_expr1.get()); + if (m_operator == OpLogicalAnd) + generator.emitJumpIfFalse(temp.get(), target.get()); + else + generator.emitJumpIfTrue(temp.get(), target.get()); + generator.emitNode(temp.get(), m_expr2.get()); + generator.emitLabel(target.get()); -bool LogicalOrNode::evaluateToBoolean(ExecState* exec) -{ - bool b = m_expr1->evaluateToBoolean(exec); - KJS_CHECKEXCEPTIONBOOLEAN - return b || m_expr2->evaluateToBoolean(exec); + return generator.moveToDestinationIfNeeded(dst, temp.get()); } // ------------------------------ ConditionalNode ------------------------------ -void ConditionalNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ConditionalNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_expr2.get()); - nodeStack.append(m_expr1.get()); - nodeStack.append(m_logical.get()); -} + RefPtr<RegisterID> newDst = generator.finalDestination(dst); + RefPtr<LabelID> beforeElse = generator.newLabel(); + RefPtr<LabelID> afterElse = generator.newLabel(); -// 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); -} + RegisterID* cond = generator.emitNode(m_logical.get()); + generator.emitJumpIfFalse(cond, beforeElse.get()); -bool ConditionalNode::evaluateToBoolean(ExecState* exec) -{ - bool b = m_logical->evaluateToBoolean(exec); - KJS_CHECKEXCEPTIONBOOLEAN - return b ? m_expr1->evaluateToBoolean(exec) : m_expr2->evaluateToBoolean(exec); -} + generator.emitNode(newDst.get(), m_expr1.get()); + generator.emitJump(afterElse.get()); -double ConditionalNode::evaluateToNumber(ExecState* exec) -{ - bool b = m_logical->evaluateToBoolean(exec); - KJS_CHECKEXCEPTIONNUMBER - return b ? m_expr1->evaluateToNumber(exec) : m_expr2->evaluateToNumber(exec); -} + generator.emitLabel(beforeElse.get()); + generator.emitNode(newDst.get(), m_expr2.get()); -int32_t ConditionalNode::evaluateToInt32(ExecState* exec) -{ - bool b = m_logical->evaluateToBoolean(exec); - KJS_CHECKEXCEPTIONNUMBER - return b ? m_expr1->evaluateToInt32(exec) : m_expr2->evaluateToInt32(exec); -} + generator.emitLabel(afterElse.get()); -uint32_t ConditionalNode::evaluateToUInt32(ExecState* exec) -{ - bool b = m_logical->evaluateToBoolean(exec); - KJS_CHECKEXCEPTIONNUMBER - return b ? m_expr1->evaluateToUInt32(exec) : m_expr2->evaluateToUInt32(exec); + return newDst.get(); } -// ECMA 11.13 +// ------------------------------ ReadModifyResolveNode ----------------------------------- -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) +// FIXME: should this be moved to be a method on CodeGenerator? +static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(CodeGenerator& generator, RegisterID* dst, RegisterID* src1, RegisterID* src2, Operator oper, OperandTypes types) { - JSValue* v; - int i1; - int i2; - unsigned int ui; + OpcodeID opcode; switch (oper) { case OpMultEq: - v = jsNumber(current->toNumber(exec) * right->evaluateToNumber(exec)); + opcode = op_mul; break; case OpDivEq: - v = jsNumber(current->toNumber(exec) / right->evaluateToNumber(exec)); + opcode = op_div; break; case OpPlusEq: - v = add(exec, current, right->evaluate(exec)); + opcode = op_add; break; case OpMinusEq: - v = jsNumber(current->toNumber(exec) - right->evaluateToNumber(exec)); + opcode = op_sub; break; case OpLShift: - i1 = current->toInt32(exec); - i2 = right->evaluateToInt32(exec); - v = jsNumber(i1 << i2); + opcode = op_lshift; break; case OpRShift: - i1 = current->toInt32(exec); - i2 = right->evaluateToInt32(exec); - v = jsNumber(i1 >> i2); + opcode = op_rshift; break; case OpURShift: - ui = current->toUInt32(exec); - i2 = right->evaluateToInt32(exec); - v = jsNumber(ui >> i2); + opcode = op_urshift; break; case OpAndEq: - i1 = current->toInt32(exec); - i2 = right->evaluateToInt32(exec); - v = jsNumber(i1 & i2); + opcode = op_bitand; break; case OpXOrEq: - i1 = current->toInt32(exec); - i2 = right->evaluateToInt32(exec); - v = jsNumber(i1 ^ i2); + opcode = op_bitxor; break; case OpOrEq: - i1 = current->toInt32(exec); - i2 = right->evaluateToInt32(exec); - v = jsNumber(i1 | i2); + opcode = op_bitor; break; - case OpModEq: { - double d1 = current->toNumber(exec); - double d2 = right->evaluateToNumber(exec); - v = jsNumber(fmod(d1, d2)); - } + case OpModEq: + opcode = op_mod; break; default: ASSERT_NOT_REACHED(); - v = jsUndefined(); + return dst; } - - return v; + + return generator.emitBinaryOp(opcode, dst, src1, src2, types); } -// ------------------------------ ReadModifyResolveNode ----------------------------------- - -void ReadModifyResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) +RegisterID* ReadModifyResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + RegisterID* src2 = generator.emitNode(m_right.get()); + return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); + } + + if (generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) { + RefPtr<RegisterID> result = generator.newTemporary(); + generator.emitMove(result.get(), local); + RegisterID* src2 = generator.emitNode(m_right.get()); + emitReadModifyAssignment(generator, result.get(), result.get(), src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); + generator.emitMove(local, result.get()); + return generator.moveToDestinationIfNeeded(dst, result.get()); + } + + RegisterID* src2 = generator.emitNode(m_right.get()); + RegisterID* result = emitReadModifyAssignment(generator, local, local, src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); + return generator.moveToDestinationIfNeeded(dst, result); } -} -// ------------------------------ 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); + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); + RegisterID* src2 = generator.emitNode(m_right.get()); + RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); + generator.emitPutScopedVar(depth, index, result, globalObject); + return result; } -} -// ------------------------------ 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; + RefPtr<RegisterID> src1 = generator.tempDestination(dst); + generator.emitExpressionInfo(m_divot - m_startOffset + m_ident.size(), m_ident.size(), 0); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), src1.get(), m_ident); + RegisterID* src2 = generator.emitNode(m_right.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); + return generator.emitPutById(base.get(), m_ident, result); } -// ------------------------------ 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 ----------------------------------- +// ------------------------------ AssignResolveNode ----------------------------------- -JSValue* ReadModifyConstNode::evaluate(ExecState* exec) +RegisterID* AssignResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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; -} + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) + return generator.emitNode(dst, m_right.get()); + + RegisterID* result = generator.emitNode(local, m_right.get()); + return generator.moveToDestinationIfNeeded(dst, result); + } -// ------------------------------ AssignConstNode ----------------------------------- + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { + if (dst == ignoredResult()) + dst = 0; + RegisterID* value = generator.emitNode(dst, m_right.get()); + generator.emitPutScopedVar(depth, index, value, globalObject); + return value; + } -JSValue* AssignConstNode::evaluate(ExecState* exec) -{ - return m_right->evaluate(exec); + RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); + if (dst == ignoredResult()) + dst = 0; + RegisterID* value = generator.emitNode(dst, m_right.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitPutById(base.get(), m_ident, value); } -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); +// ------------------------------ AssignDotNode ----------------------------------- -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) +RegisterID* AssignDotNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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; + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base.get(), m_rightHasAssignments, m_right->isPure(generator)); + RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); + RegisterID* result = generator.emitNode(value.get(), m_right.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutById(base.get(), m_ident, result); + return generator.moveToDestinationIfNeeded(dst, result); } // ------------------------------ 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) +RegisterID* ReadModifyDotNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* baseValue = m_base->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - JSObject* base = baseValue->toObject(exec); + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base.get(), m_rightHasAssignments, m_right->isPure(generator)); - JSValue* v; + generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RegisterID* change = generator.emitNode(m_right.get()); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), change, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); - 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; + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + return generator.emitPutById(base.get(), m_ident, updatedValue); } // ------------------------------ AssignErrorNode ----------------------------------- -JSValue* AssignErrorNode::evaluate(ExecState* exec) +RegisterID* AssignErrorNode::emitCode(CodeGenerator& generator, RegisterID*) { - throwError(exec, ReferenceError, "Left side of assignment is not a reference."); - handleException(exec); - return jsUndefined(); + return emitThrowError(generator, ReferenceError, "Left side of assignment is not a reference."); } // ------------------------------ 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) +RegisterID* AssignBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* baseValue = m_base->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - JSValue* subscript = m_subscript->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - - JSObject* base = baseValue->toObject(exec); + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base.get(), m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); + RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript.get(), m_rightHasAssignments, m_right->isPure(generator)); + RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); + RegisterID* result = generator.emitNode(value.get(), m_right.get()); - 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()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutByVal(base.get(), property.get(), result); + return generator.moveToDestinationIfNeeded(dst, result); } -JSValue* ReadModifyBracketNode::evaluate(ExecState* exec) +RegisterID* ReadModifyBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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; + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base.get(), m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); + RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript.get(), m_rightHasAssignments, m_right->isPure(generator)); - 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); + generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); + RegisterID* change = generator.emitNode(m_right.get()); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), change, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor())); - KJS_CHECKEXCEPTIONVALUE + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutByVal(base.get(), property.get(), updatedValue); - base->put(exec, propertyName, v); - return v; + return updatedValue; } // ------------------------------ 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) +RegisterID* CommaNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - m_expr1->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - return m_expr2->evaluate(exec); + generator.emitNode(ignoredResult(), m_expr1.get()); + return generator.emitNode(dst, m_expr2.get()); } // ------------------------------ ConstDeclNode ---------------------------------- -ConstDeclNode::ConstDeclNode(const Identifier& ident, ExpressionNode* init) - : m_ident(ident) +ConstDeclNode::ConstDeclNode(JSGlobalData* globalData, const Identifier& ident, ExpressionNode* init) + : ExpressionNode(globalData) + , m_ident(ident) , m_init(init) { } -void ConstDeclNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ConstDeclNode::emitCodeSingle(CodeGenerator& generator) { - 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; + if (RegisterID* local = generator.constRegisterFor(m_ident)) { + if (!m_init) + return local; - do { - base = *iter; - if (base->getPropertySlot(exec, m_ident, slot)) - break; - - ++iter; - } while (iter != end); - - unsigned flags = 0; - base->getPropertyAttributes(m_ident, flags); - flags |= ReadOnly; - - base->put(exec, m_ident, val, flags); -} - -// 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(); - JSObject* variableObject = exec->variableObject(); - - ASSERT(!chain.isEmpty()); - - bool inGlobalScope = ++chain.begin() == chain.end(); - - if (m_init) { - if (inGlobalScope) { - JSValue* val = m_init->evaluate(exec); - int flags = Internal; - if (exec->codeType() != EvalCode) - flags |= DontDelete; - flags |= ReadOnly; - variableObject->put(exec, m_ident, val, flags); - } 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); - - unsigned flags = 0; - variableObject->getPropertyAttributes(m_ident, flags); - flags |= ReadOnly; - - variableObject->put(exec, m_ident, val, flags); - } + return generator.emitNode(local, m_init.get()); } + + // FIXME: While this code should only be hit in eval code, it will potentially + // assign to the wrong base if m_ident exists in an intervening dynamic scope. + RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); + RegisterID* value = m_init ? generator.emitNode(m_init.get()) : generator.emitLoad(0, jsUndefined()); + return generator.emitPutById(base.get(), m_ident, value); } -JSValue* ConstDeclNode::evaluate(ExecState* exec) +RegisterID* ConstDeclNode::emitCode(CodeGenerator& generator, RegisterID*) { - evaluateSingle(exec); - - if (ConstDeclNode* n = m_next.get()) { - do { - n->evaluateSingle(exec); - KJS_CHECKEXCEPTIONVALUE - n = n->m_next.get(); - } while (n); - } - return jsUndefined(); + RegisterID* result = 0; + for (ConstDeclNode* n = this; n; n = n->m_next.get()) + result = n->emitCodeSingle(generator); + + return result; } // ------------------------------ ConstStatementNode ----------------------------- -void ConstStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ConstStatementNode::emitCode(CodeGenerator& generator, RegisterID*) { - 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(); + return generator.emitNode(m_next.get()); } // ------------------------------ Helper functions for handling Vectors of StatementNode ------------------------------- -static inline void statementListPushFIFO(StatementVector& statements, DeclarationStacks::NodeStack& stack) +static inline RegisterID* statementListEmitCode(StatementVector& statements, CodeGenerator& generator, RegisterID* dst) { - StatementVector::iterator it = statements.end(); - StatementVector::iterator begin = statements.begin(); - while (it != begin) { - --it; - stack.append((*it).get()); + StatementVector::iterator end = statements.end(); + for (StatementVector::iterator it = statements.begin(); it != end; ++it) { + StatementNode* n = it->get(); + if (!n->isLoop()) + generator.emitDebugHook(WillExecuteStatement, n->firstLine(), n->lastLine()); + generator.emitNode(dst, n); } + return 0; } -static inline Node* statementListInitializeVariableAccessStack(StatementVector& statements, DeclarationStacks::NodeStack& stack) +static inline void statementListPushFIFO(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) { + while (it != begin) { --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) +BlockNode::BlockNode(JSGlobalData* globalData, SourceElements* children) + : StatementNode(globalData) { if (children) children->releaseContentsIntoVector(m_children); } -void BlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* BlockNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - statementListPushFIFO(m_children, nodeStack); + return statementListEmitCode(m_children, generator, dst); } -// ECMA 12.1 -JSValue* BlockNode::execute(ExecState* exec) +// ------------------------------ EmptyStatementNode --------------------------- + +RegisterID* EmptyStatementNode::emitCode(CodeGenerator&, RegisterID* dst) { - return statementListExecute(m_children, exec); + return dst; } -// ------------------------------ EmptyStatementNode --------------------------- +// ------------------------------ DebuggerStatementNode --------------------------- -// ECMA 12.3 -JSValue* EmptyStatementNode::execute(ExecState* exec) +RegisterID* DebuggerStatementNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - return exec->setNormalCompletion(); + generator.emitDebugHook(DidReachBreakpoint, firstLine(), lastLine()); + return dst; } // ------------------------------ ExprStatementNode ---------------------------- -void ExprStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ExprStatementNode::emitCode(CodeGenerator& generator, RegisterID* dst) { 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); + return generator.emitNode(dst, m_expr.get()); } // ------------------------------ VarStatementNode ---------------------------- -void VarStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* VarStatementNode::emitCode(CodeGenerator& generator, RegisterID*) { ASSERT(m_expr); - nodeStack.append(m_expr.get()); + return generator.emitNode(m_expr.get()); } -JSValue* VarStatementNode::execute(ExecState* exec) +// ------------------------------ IfNode --------------------------------------- + +RegisterID* IfNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - m_expr->evaluate(exec); - KJS_CHECKEXCEPTION + RefPtr<LabelID> afterThen = generator.newLabel(); - return exec->setNormalCompletion(); -} + RegisterID* cond = generator.emitNode(m_condition.get()); + generator.emitJumpIfFalse(cond, afterThen.get()); -// ------------------------------ IfNode --------------------------------------- + if (!m_ifBlock->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_ifBlock->firstLine(), m_ifBlock->lastLine()); -void IfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) -{ - nodeStack.append(m_ifBlock.get()); - nodeStack.append(m_condition.get()); + generator.emitNode(dst, m_ifBlock.get()); + generator.emitLabel(afterThen.get()); + + // FIXME: This should return the last statement exectuted so that it can be returned as a Completion + return 0; } -// ECMA 12.5 -JSValue* IfNode::execute(ExecState* exec) +RegisterID* IfElseNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - bool b = m_condition->evaluateToBoolean(exec); - KJS_CHECKEXCEPTION + RefPtr<LabelID> beforeElse = generator.newLabel(); + RefPtr<LabelID> afterElse = generator.newLabel(); - if (b) - return m_ifBlock->execute(exec); - return exec->setNormalCompletion(); -} + RegisterID* cond = generator.emitNode(m_condition.get()); + generator.emitJumpIfFalse(cond, beforeElse.get()); -void IfElseNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) -{ - nodeStack.append(m_elseBlock.get()); - IfNode::optimizeVariableAccess(symbolTable, localStorage, nodeStack); -} + if (!m_ifBlock->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_ifBlock->firstLine(), m_ifBlock->lastLine()); -// ECMA 12.5 -JSValue* IfElseNode::execute(ExecState* exec) -{ - bool b = m_condition->evaluateToBoolean(exec); - KJS_CHECKEXCEPTION + generator.emitNode(dst, m_ifBlock.get()); + generator.emitJump(afterElse.get()); - if (b) - return m_ifBlock->execute(exec); - return m_elseBlock->execute(exec); -} + generator.emitLabel(beforeElse.get()); -// ------------------------------ DoWhileNode ---------------------------------- + if (!m_elseBlock->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_elseBlock->firstLine(), m_elseBlock->lastLine()); -void DoWhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) -{ - nodeStack.append(m_statement.get()); - nodeStack.append(m_expr.get()); + generator.emitNode(dst, m_elseBlock.get()); + + generator.emitLabel(afterElse.get()); + + // FIXME: This should return the last statement exectuted so that it can be returned as a Completion + return 0; } -// ECMA 12.6.1 -JSValue* DoWhileNode::execute(ExecState* exec) +// ------------------------------ DoWhileNode ---------------------------------- + +RegisterID* DoWhileNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* value = 0; + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - while (1) { - exec->pushIteration(); - JSValue* statementValue = m_statement->execute(exec); - exec->popIteration(); + RefPtr<LabelID> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); - if (exec->dynamicGlobalObject()->timedOut()) - return exec->setInterruptedCompletion(); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - if (statementValue) - value = statementValue; + if (!m_statement->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_statement->firstLine(), m_statement->lastLine()); + + RefPtr<RegisterID> result = generator.emitNode(dst, m_statement.get()); - 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; - } + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); + RegisterID* cond = generator.emitNode(m_expr.get()); + generator.emitJumpIfTrue(cond, topOfLoop.get()); - continueDoWhileLoop: - bool b = m_expr->evaluateToBoolean(exec); - KJS_CHECKEXCEPTION - if (!b) - break; - } - - return exec->setNormalCompletion(value); + generator.emitLabel(scope->breakTarget()); + return result.get(); } // ------------------------------ WhileNode ------------------------------------ -void WhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* WhileNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_statement.get()); - nodeStack.append(m_expr.get()); -} + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); -// 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; + generator.emitJump(scope->continueTarget()); - exec->pushIteration(); - JSValue* statementValue = m_statement->execute(exec); - exec->popIteration(); + RefPtr<LabelID> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); - if (exec->dynamicGlobalObject()->timedOut()) - return exec->setInterruptedCompletion(); + if (!m_statement->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_statement->firstLine(), m_statement->lastLine()); + + generator.emitNode(dst, m_statement.get()); - if (statementValue) - value = statementValue; + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); + RegisterID* cond = generator.emitNode(m_expr.get()); + generator.emitJumpIfTrue(cond, topOfLoop.get()); - 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); + generator.emitLabel(scope->breakTarget()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion + return 0; } // ------------------------------ ForNode -------------------------------------- -void ForNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* ForNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_statement.get()); - nodeStack.append(m_expr3.get()); - nodeStack.append(m_expr2.get()); - nodeStack.append(m_expr1.get()); -} + if (dst == ignoredResult()) + dst = 0; -// ECMA 12.6.3 -JSValue* ForNode::execute(ExecState* exec) -{ - JSValue* value = 0; + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - m_expr1->evaluate(exec); - KJS_CHECKEXCEPTION + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - while (1) { - bool b = m_expr2->evaluateToBoolean(exec); - KJS_CHECKEXCEPTION - if (!b) - break; + if (m_expr1) + generator.emitNode(ignoredResult(), m_expr1.get()); - exec->pushIteration(); - JSValue* statementValue = m_statement->execute(exec); - exec->popIteration(); - if (statementValue) - value = statementValue; + RefPtr<LabelID> condition = generator.newLabel(); + generator.emitJump(condition.get()); - if (exec->dynamicGlobalObject()->timedOut()) - return exec->setInterruptedCompletion(); + RefPtr<LabelID> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); - 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; - } + if (!m_statement->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_statement->firstLine(), m_statement->lastLine()); + RefPtr<RegisterID> result = generator.emitNode(dst, m_statement.get()); - continueForLoop: - m_expr3->evaluate(exec); - KJS_CHECKEXCEPTION - } + generator.emitLabel(scope->continueTarget()); + if (m_expr3) + generator.emitNode(ignoredResult(), m_expr3.get()); - return exec->setNormalCompletion(value); + generator.emitLabel(condition.get()); + if (m_expr2) { + RegisterID* cond = generator.emitNode(m_expr2.get()); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } else + generator.emitJump(topOfLoop.get()); + + generator.emitLabel(scope->breakTarget()); + return result.get(); } // ------------------------------ ForInNode ------------------------------------ -ForInNode::ForInNode(ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) - : m_init(0L) +ForInNode::ForInNode(JSGlobalData* globalData, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) + : StatementNode(globalData) + , m_init(0L) , m_lexpr(l) , m_expr(expr) , m_statement(statement) @@ -3934,791 +1274,609 @@ ForInNode::ForInNode(ExpressionNode* l, ExpressionNode* expr, StatementNode* sta { } -ForInNode::ForInNode(const Identifier& ident, ExpressionNode* in, ExpressionNode* expr, StatementNode* statement) - : m_ident(ident) - , m_lexpr(new ResolveNode(ident)) +ForInNode::ForInNode(JSGlobalData* globalData, const Identifier& ident, ExpressionNode* in, ExpressionNode* expr, StatementNode* statement, int divot, int startOffset, int endOffset) + : StatementNode(globalData) + , m_ident(ident) + , m_lexpr(new ResolveNode(globalData, ident, divot - startOffset)) , m_expr(expr) , m_statement(statement) , m_identIsVarDecl(true) { - if (in) - m_init = new AssignResolveNode(ident, in); + if (in) { + AssignResolveNode* node = new AssignResolveNode(globalData, ident, in, true); + node->setExceptionSourceCode(divot, divot - startOffset, endOffset - divot); + m_init = node; + } // 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) +RegisterID* ForInNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - 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); - } + if (!m_lexpr->isLocation()) + return emitThrowError(generator, ReferenceError, "Left side of for-in statement is not a reference."); - KJS_CHECKEXCEPTION + RefPtr<LabelID> continueTarget = generator.newLabel(); - exec->pushIteration(); - JSValue* statementValue = m_statement->execute(exec); - exec->popIteration(); - if (statementValue) - value = statementValue; + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - 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; + if (m_init) + generator.emitNode(ignoredResult(), m_init.get()); + RegisterID* forInBase = generator.emitNode(m_expr.get()); + RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), forInBase); + generator.emitJump(scope->continueTarget()); + + RefPtr<LabelID> loopStart = generator.newLabel(); + generator.emitLabel(loopStart.get()); + + RegisterID* propertyName; + if (m_lexpr->isResolveNode()) { + const Identifier& ident = static_cast<ResolveNode*>(m_lexpr.get())->identifier(); + propertyName = generator.registerFor(ident); + if (!propertyName) { + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RegisterID* base = generator.emitResolveBase(generator.newTemporary(), ident); + + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitPutById(base, ident, propertyName); } - } - - return exec->setNormalCompletion(value); + } else if (m_lexpr->isDotAccessorNode()) { + DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr.get()); + const Identifier& ident = assignNode->identifier(); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RegisterID* base = generator.emitNode(assignNode->base()); + + generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); + generator.emitPutById(base, ident, propertyName); + } else { + ASSERT(m_lexpr->isBracketAccessorNode()); + BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr.get()); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RefPtr<RegisterID> base = generator.emitNode(assignNode->base()); + RegisterID* subscript = generator.emitNode(assignNode->subscript()); + + generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); + generator.emitPutByVal(base.get(), subscript, propertyName); + } + + if (!m_statement->isBlock()) + generator.emitDebugHook(WillExecuteStatement, m_statement->firstLine(), m_statement->lastLine()); + generator.emitNode(dst, m_statement.get()); + + generator.emitLabel(scope->continueTarget()); + generator.emitNextPropertyName(propertyName, iter.get(), loopStart.get()); + generator.emitLabel(scope->breakTarget()); + return dst; } // ------------------------------ ContinueNode --------------------------------- // ECMA 12.7 -JSValue* ContinueNode::execute(ExecState* exec) +RegisterID* ContinueNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + LabelScope* scope = generator.continueTarget(m_ident); + + if (!scope) + return m_ident.isEmpty() + ? emitThrowError(generator, SyntaxError, "Invalid continue statement.") + : emitThrowError(generator, SyntaxError, "Undefined label: '%s'.", m_ident); + + generator.emitJumpScopes(scope->continueTarget(), scope->scopeDepth()); + return dst; } // ------------------------------ BreakNode ------------------------------------ // ECMA 12.8 -JSValue* BreakNode::execute(ExecState* exec) +RegisterID* BreakNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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); + LabelScope* scope = generator.breakTarget(m_ident); + + if (!scope) + return m_ident.isEmpty() + ? emitThrowError(generator, SyntaxError, "Invalid break statement.") + : emitThrowError(generator, SyntaxError, "Undefined label: '%s'.", m_ident); + + generator.emitJumpScopes(scope->breakTarget(), scope->scopeDepth()); + return dst; } // ------------------------------ 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) +RegisterID* ReturnNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - 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 + if (generator.codeType() != FunctionCode) + return emitThrowError(generator, SyntaxError, "Invalid return statement."); - return exec->setReturnValueCompletion(v); + if (dst == ignoredResult()) + dst = 0; + RegisterID* r0 = m_value ? generator.emitNode(dst, m_value.get()) : generator.emitLoad(dst, jsUndefined()); + if (generator.scopeDepth()) { + RefPtr<LabelID> l0 = generator.newLabel(); + generator.emitJumpScopes(l0.get(), 0); + generator.emitLabel(l0.get()); + } + generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); + return generator.emitReturn(r0); } // ------------------------------ 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) +RegisterID* WithNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - if (m_expr) - nodeStack.append(m_expr.get()); - statementListPushFIFO(m_children, nodeStack); + RefPtr<RegisterID> scope = generator.emitNode(m_expr.get()); // scope must be protected until popped + generator.emitExpressionInfo(m_divot, m_expressionLength, 0); + generator.emitPushScope(scope.get()); + RegisterID* result = generator.emitNode(dst, m_statement.get()); + generator.emitPopScope(); + return result; } -// ECMA 12.11 -JSValue* CaseClauseNode::evaluate(ExecState* exec) -{ - JSValue* v = m_expr->evaluate(exec); - KJS_CHECKEXCEPTIONVALUE - - return v; -} +// ------------------------------ CaseBlockNode -------------------------------- +enum SwitchKind { + SwitchUnset = 0, + SwitchNumber = 1, + SwitchString = 2, + SwitchNeither = 3 +}; -// ECMA 12.11 -JSValue* CaseClauseNode::executeStatements(ExecState* exec) +static void processClauseList(ClauseListNode* list, Vector<ExpressionNode*, 8>& literalVector, SwitchKind& typeForTable, bool& singleCharacterSwitch, int32_t& min_num, int32_t& max_num) { - return statementListExecute(m_children, exec); + for (; list; list = list->getNext()) { + ExpressionNode* clauseExpression = list->getClause()->expr(); + literalVector.append(clauseExpression); + if (clauseExpression->isNumber()) { + double value = static_cast<NumberNode*>(clauseExpression)->value(); + if ((typeForTable & ~SwitchNumber) || !JSImmediate::from(value)) { + typeForTable = SwitchNeither; + break; + } + int32_t intVal = static_cast<int32_t>(value); + ASSERT(intVal == value); + if (intVal < min_num) + min_num = intVal; + if (intVal > max_num) + max_num = intVal; + typeForTable = SwitchNumber; + continue; + } + if (clauseExpression->isString()) { + if (typeForTable & ~SwitchString) { + typeForTable = SwitchNeither; + break; + } + const UString& value = static_cast<StringNode*>(clauseExpression)->value().ustring(); + if (singleCharacterSwitch &= value.size() == 1) { + int32_t intVal = value.rep()->data()[0]; + if (intVal < min_num) + min_num = intVal; + if (intVal > max_num) + max_num = intVal; + } + typeForTable = SwitchString; + continue; + } + typeForTable = SwitchNeither; + break; + } } - -// ------------------------------ ClauseListNode ------------------------------- - -void ClauseListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) + +SwitchInfo::SwitchType CaseBlockNode::tryOptimizedSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num) { - if (m_next) - nodeStack.append(m_next.get()); - nodeStack.append(m_clause.get()); -} - -// ------------------------------ CaseBlockNode -------------------------------- + SwitchKind typeForTable = SwitchUnset; + bool singleCharacterSwitch = true; + + processClauseList(m_list1.get(), literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); + processClauseList(m_list2.get(), literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); + + if (typeForTable == SwitchUnset || typeForTable == SwitchNeither) + return SwitchInfo::SwitchNone; + + if (typeForTable == SwitchNumber) { + int32_t range = max_num - min_num; + if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) + return SwitchInfo::SwitchImmediate; + return SwitchInfo::SwitchNone; + } + + ASSERT(typeForTable == SwitchString); + + if (singleCharacterSwitch) { + int32_t range = max_num - min_num; + if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) + return SwitchInfo::SwitchCharacter; + } -CaseBlockNode::CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) - : m_list1(list1) - , m_defaultClause(defaultClause) - , m_list2(list2) -{ + return SwitchInfo::SwitchString; } -void CaseBlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* CaseBlockNode::emitCodeForBlock(CodeGenerator& generator, RegisterID* switchExpression, RegisterID* dst) { - if (m_list2) - nodeStack.append(m_list2.get()); - if (m_defaultClause) - nodeStack.append(m_defaultClause.get()); - if (m_list1) - nodeStack.append(m_list1.get()); -} + RefPtr<LabelID> defaultLabel; + Vector<RefPtr<LabelID>, 8> labelVector; + Vector<ExpressionNode*, 8> literalVector; + int32_t min_num = std::numeric_limits<int32_t>::max(); + int32_t max_num = std::numeric_limits<int32_t>::min(); + SwitchInfo::SwitchType switchType = tryOptimizedSwitch(literalVector, min_num, max_num); -// 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; + if (switchType != SwitchInfo::SwitchNone) { + // Prepare the various labels + for (uint32_t i = 0; i < literalVector.size(); i++) + labelVector.append(generator.newLabel()); + defaultLabel = generator.newLabel(); + generator.beginSwitch(switchExpression, switchType); + } else { + // Setup jumps + for (ClauseListNode* list = m_list1.get(); list; list = list->getNext()) { + RefPtr<RegisterID> clauseVal = generator.newTemporary(); + generator.emitNode(clauseVal.get(), list->getClause()->expr()); + generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); + labelVector.append(generator.newLabel()); + generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); + } + + for (ClauseListNode* list = m_list2.get(); list; list = list->getNext()) { + RefPtr<RegisterID> clauseVal = generator.newTemporary(); + generator.emitNode(clauseVal.get(), list->getClause()->expr()); + generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); + labelVector.append(generator.newLabel()); + generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); } + defaultLabel = generator.newLabel(); + generator.emitJump(defaultLabel.get()); } - 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; - } + RegisterID* result = 0; + + size_t i = 0; + for (ClauseListNode* list = m_list1.get(); list; list = list->getNext()) { + generator.emitLabel(labelVector[i++].get()); + result = statementListEmitCode(list->getClause()->children(), generator, dst); } - // 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(); + generator.emitLabel(defaultLabel.get()); + result = statementListEmitCode(m_defaultClause->children(), generator, dst); } - // bail out on error - KJS_CHECKEXCEPTION + for (ClauseListNode* list = m_list2.get(); list; list = list->getNext()) { + generator.emitLabel(labelVector[i++].get()); + result = statementListEmitCode(list->getClause()->children(), generator, dst); + } + if (!m_defaultClause) + generator.emitLabel(defaultLabel.get()); - return exec->setNormalCompletion(); + ASSERT(i == labelVector.size()); + if (switchType != SwitchInfo::SwitchNone) { + ASSERT(labelVector.size() == literalVector.size()); + generator.endSwitch(labelVector.size(), labelVector.data(), literalVector.data(), defaultLabel.get(), min_num, max_num); + } + return result; } // ------------------------------ 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) +RegisterID* SwitchNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v = m_expr->evaluate(exec); - KJS_CHECKEXCEPTION + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Switch); - exec->pushSwitch(); - JSValue* result = m_block->executeBlock(exec, v); - exec->popSwitch(); + RefPtr<RegisterID> r0 = generator.emitNode(m_expr.get()); + RegisterID* r1 = m_block->emitCodeForBlock(generator, r0.get(), dst); - if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) - exec->setCompletionType(Normal); - return result; + generator.emitLabel(scope->breakTarget()); + return r1; } // ------------------------------ LabelNode ------------------------------------ -void LabelNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* LabelNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - nodeStack.append(m_statement.get()); -} + if (generator.breakTarget(m_name)) + return emitThrowError(generator, SyntaxError, "Duplicate label: %s.", m_name); -// 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(); + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::NamedLabel, &m_name); + RegisterID* r0 = generator.emitNode(dst, m_statement.get()); - if (exec->completionType() == Break && exec->breakOrContinueTarget() == m_label) - exec->setCompletionType(Normal); - return result; + generator.emitLabel(scope->breakTarget()); + return r0; } // ------------------------------ ThrowNode ------------------------------------ -void ThrowNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) -{ - nodeStack.append(m_expr.get()); -} - -// ECMA 12.13 -JSValue* ThrowNode::execute(ExecState* exec) +RegisterID* ThrowNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - JSValue* v = m_expr->evaluate(exec); - KJS_CHECKEXCEPTION - - handleException(exec, v); - return exec->setThrowCompletion(v); + if (dst == ignoredResult()) + dst = 0; + RefPtr<RegisterID> expr = generator.emitNode(dst, m_expr.get()); + generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset); + generator.emitThrow(expr.get()); + return dst; } // ------------------------------ TryNode -------------------------------------- -void TryNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) +RegisterID* TryNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - // Can't optimize within catchBlock because "catch" introduces a dynamic scope. - if (m_finallyBlock) - nodeStack.append(m_finallyBlock.get()); - nodeStack.append(m_tryBlock.get()); -} + RefPtr<LabelID> tryStartLabel = generator.newLabel(); + RefPtr<LabelID> tryEndLabel = generator.newLabel(); + RefPtr<LabelID> finallyStart; + RefPtr<RegisterID> finallyReturnAddr; + if (m_finallyBlock) { + finallyStart = generator.newLabel(); + finallyReturnAddr = generator.newTemporary(); + generator.pushFinallyContext(finallyStart.get(), finallyReturnAddr.get()); + } + generator.emitLabel(tryStartLabel.get()); + generator.emitNode(dst, m_tryBlock.get()); + generator.emitLabel(tryEndLabel.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->put(exec, m_exceptionIdent, result, DontDelete); - exec->dynamicGlobalObject()->tearOffActivation(exec); - exec->pushScope(obj); - result = m_catchBlock->execute(exec); - exec->popScope(); + if (m_catchBlock) { + RefPtr<LabelID> handlerEndLabel = generator.newLabel(); + generator.emitJump(handlerEndLabel.get()); + RefPtr<RegisterID> exceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), tryEndLabel.get()); + generator.emitPushNewScope(exceptionRegister.get(), m_exceptionIdent, exceptionRegister.get()); + generator.emitNode(dst, m_catchBlock.get()); + generator.emitPopScope(); + generator.emitLabel(handlerEndLabel.get()); } if (m_finallyBlock) { - ComplType savedCompletionType = exec->completionType(); - JSValue* finallyResult = m_finallyBlock->execute(exec); - if (exec->completionType() != Normal) - result = finallyResult; - else - exec->setCompletionType(savedCompletionType); + generator.popFinallyContext(); + // there may be important registers live at the time we jump + // to a finally block (such as for a return or throw) so we + // ref the highest register ever used as a conservative + // approach to not clobbering anything important + RefPtr<RegisterID> highestUsedRegister = generator.highestUsedRegister(); + RefPtr<LabelID> finallyEndLabel = generator.newLabel(); + generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); + // Use a label to record the subtle fact that sret will return to the + // next instruction. sret is the only way to jump without an explicit label. + generator.emitLabel(generator.newLabel().get()); + generator.emitJump(finallyEndLabel.get()); + + // Finally block for exception path + RefPtr<RegisterID> tempExceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), generator.emitLabel(generator.newLabel().get()).get()); + generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); + // Use a label to record the subtle fact that sret will return to the + // next instruction. sret is the only way to jump without an explicit label. + generator.emitLabel(generator.newLabel().get()); + generator.emitThrow(tempExceptionRegister.get()); + + // emit the finally block itself + generator.emitLabel(finallyStart.get()); + generator.emitNode(dst, m_finallyBlock.get()); + generator.emitSubroutineReturn(finallyReturnAddr.get()); + + generator.emitLabel(finallyEndLabel.get()); } - return result; + return dst; } -// ------------------------------ FunctionBodyNode ----------------------------- -ScopeNode::ScopeNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) - : BlockNode(children) - , m_sourceURL(parser().sourceURL()) - , m_sourceId(parser().sourceId()) +// ------------------------------ ScopeNode ----------------------------- + +ScopeNode::ScopeNode(JSGlobalData* globalData, const SourceCode& source, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, CodeFeatures features, int numConstants) + : BlockNode(globalData, children) + , m_source(source) + , m_features(features) + , m_numConstants(numConstants) { if (varStack) m_varStack = *varStack; if (funcStack) m_functionStack = *funcStack; +#if ENABLE(OPCODE_SAMPLING) + globalData->machine->sampler()->notifyOfScope(this); +#endif } // ------------------------------ ProgramNode ----------------------------- -ProgramNode::ProgramNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) - : ScopeNode(children, varStack, funcStack) +ProgramNode::ProgramNode(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& source, CodeFeatures features, int numConstants) + : ScopeNode(globalData, source, children, varStack, funcStack, features, numConstants) { } -ProgramNode* ProgramNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) +ProgramNode* ProgramNode::create(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& source, CodeFeatures features, int numConstants) { - return new ProgramNode(children, varStack, funcStack); + return new ProgramNode(globalData, children, varStack, funcStack, source, features, numConstants); } // ------------------------------ EvalNode ----------------------------- -EvalNode::EvalNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) - : ScopeNode(children, varStack, funcStack) +EvalNode::EvalNode(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& source, CodeFeatures features, int numConstants) + : ScopeNode(globalData, source, children, varStack, funcStack, features, numConstants) { } -EvalNode* EvalNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) +RegisterID* EvalNode::emitCode(CodeGenerator& generator, RegisterID*) { - return new EvalNode(children, varStack, funcStack); -} + generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); -// ------------------------------ FunctionBodyNode ----------------------------- + RefPtr<RegisterID> dstRegister = generator.newTemporary(); + generator.emitLoad(dstRegister.get(), jsUndefined()); + statementListEmitCode(m_children, generator, dstRegister.get()); -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); + generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); + generator.emitEnd(dstRegister.get()); + return 0; } -void FunctionBodyNode::initializeSymbolTable(ExecState* exec) +void EvalNode::generateCode(ScopeChainNode* scopeChainNode) { - SymbolTable& symbolTable = exec->variableObject()->symbolTable(); - ASSERT(symbolTable.isEmpty()); - - size_t localStorageIndex = 0; - - // Order must match the order in processDeclarations. + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); - 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); - } + SymbolTable symbolTable; + m_code.set(new EvalCodeBlock(this, globalObject, source().provider())); - 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); - } + CodeGenerator generator(this, globalObject->debugger(), scopeChain, &symbolTable, m_code.get()); + generator.generate(); } -void ProgramNode::initializeSymbolTable(ExecState* exec) +EvalNode* EvalNode::create(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& source, CodeFeatures features, int numConstants) { - // 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; - } + return new EvalNode(globalData, children, varStack, funcStack, source, features, numConstants); +} - 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; - } +// ------------------------------ FunctionBodyNode ----------------------------- - m_varIndexes[i] = result.first->second; - ++localStorageIndex; - } +FunctionBodyNode::FunctionBodyNode(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& sourceCode, CodeFeatures features, int numConstants) + : ScopeNode(globalData, sourceCode, children, varStack, funcStack, features, numConstants) + , m_parameters(0) + , m_parameterCount(0) + , m_refCount(0) +{ } -void ScopeNode::optimizeVariableAccess(ExecState* exec) +FunctionBodyNode::~FunctionBodyNode() { - 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); - } + if (m_parameters) + fastFree(m_parameters); } -void FunctionBodyNode::processDeclarations(ExecState* exec) +void FunctionBodyNode::finishParsing(const SourceCode& source, ParameterNode* firstParameter) { - 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); + Vector<Identifier> parameters; + for (ParameterNode* parameter = firstParameter; parameter; parameter = parameter->nextParam()) + parameters.append(parameter->ident()); + size_t count = parameters.size(); - int minAttributes = Internal | 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; - } + setSource(source); + finishParsing(parameters.releaseBuffer(), count); } -static void gccIsCrazy() KJS_FAST_CALL; -static void gccIsCrazy() +void FunctionBodyNode::finishParsing(Identifier* parameters, size_t parameterCount) { + ASSERT(!source().isNull()); + m_parameters = parameters; + m_parameterCount = parameterCount; } -void ProgramNode::processDeclarations(ExecState* exec) +void FunctionBodyNode::mark() { - // 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 = Internal | DontDelete; + if (m_code) + m_code->mark(); +} - // In order for our localStorage indexes to be correct, we must match the - // order of addition in initializeSymbolTable(). +FunctionBodyNode* FunctionBodyNode::create(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, CodeFeatures features, int numConstants) +{ + return new FunctionBodyNode(globalData, children, varStack, funcStack, SourceCode(), features, numConstants); +} - 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]; +FunctionBodyNode* FunctionBodyNode::create(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& sourceCode, CodeFeatures features, int numConstants) +{ + return new FunctionBodyNode(globalData, children, varStack, funcStack, sourceCode, features, numConstants); +} - if (index == localStorage.size()) - localStorage.uncheckedAppend(entry); - else { - ASSERT(index < localStorage.size()); - localStorage[index] = entry; - } - } +void FunctionBodyNode::generateCode(ScopeChainNode* scopeChainNode) +{ + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); - for (size_t i = 0, size = m_varStack.size(); i < size; ++i) { - size_t index = m_varIndexes[i]; - if (index == missingSymbolMarker()) - continue; + m_code.set(new CodeBlock(this, FunctionCode, source().provider(), source().startOffset())); - int attributes = minAttributes; - if (m_varStack[i].second & DeclarationStacks::IsConstant) - attributes |= ReadOnly; - LocalStorageEntry entry = LocalStorageEntry(jsUndefined(), attributes); + CodeGenerator generator(this, globalObject->debugger(), scopeChain, &m_symbolTable, m_code.get()); + generator.generate(); +} - ASSERT(index == localStorage.size()); - localStorage.uncheckedAppend(entry); +RegisterID* FunctionBodyNode::emitCode(CodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(DidEnterCallFrame, firstLine(), lastLine()); + statementListEmitCode(m_children, generator, ignoredResult()); + if (!m_children.size() || !m_children.last()->isReturnNode()) { + RegisterID* r0 = generator.emitLoad(0, jsUndefined()); + generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); + generator.emitReturn(r0); } - - optimizeVariableAccess(exec); + return 0; } -void EvalNode::processDeclarations(ExecState* exec) +RegisterID* ProgramNode::emitCode(CodeGenerator& generator, RegisterID*) { - // 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(); + generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); - int minAttributes = Internal; + RefPtr<RegisterID> dstRegister = generator.newTemporary(); + generator.emitLoad(dstRegister.get(), jsUndefined()); + statementListEmitCode(m_children, generator, dstRegister.get()); - for (i = 0, size = m_varStack.size(); i < size; ++i) { - Identifier& ident = m_varStack[i].first; - if (variableObject->hasProperty(exec, ident)) - continue; - int attributes = minAttributes; - if (m_varStack[i].second & DeclarationStacks::IsConstant) - attributes |= ReadOnly; - variableObject->put(exec, ident, jsUndefined(), attributes); - } + generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); + generator.emitEnd(dstRegister.get()); + return 0; +} - for (i = 0, size = m_functionStack.size(); i < size; ++i) { - FuncDeclNode* node = m_functionStack[i]; - variableObject->put(exec, node->m_ident, node->makeFunction(exec), minAttributes); - } +void ProgramNode::generateCode(ScopeChainNode* scopeChainNode) +{ + ScopeChain scopeChain(scopeChainNode); + JSGlobalObject* globalObject = scopeChain.globalObject(); + + m_code.set(new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider())); + + CodeGenerator generator(this, globalObject->debugger(), scopeChain, &globalObject->symbolTable(), m_code.get(), m_varStack, m_functionStack); + generator.generate(); } UString FunctionBodyNode::paramString() const { UString s(""); - size_t count = m_parameters.size(); - for (size_t pos = 0; pos < count; ++pos) { + for (size_t pos = 0; pos < m_parameterCount; ++pos) { if (!s.isEmpty()) s += ", "; - s += m_parameters[pos].ustring(); + s += parameters()[pos].ustring(); } return s; } -JSValue* ProgramNode::execute(ExecState* exec) +Identifier* FunctionBodyNode::copyParameters() { - 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())) { - dbg->imp()->abort(); - 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())) { - dbg->imp()->abort(); - return exec->setInterruptedCompletion(); - } - } - - return result; + Identifier* parameters = static_cast<Identifier*>(fastMalloc(m_parameterCount * sizeof(Identifier))); + VectorCopier<false, Identifier>::uninitializedCopy(m_parameters, m_parameters + m_parameterCount, parameters); + return parameters; } // ------------------------------ FuncDeclNode --------------------------------- -void FuncDeclNode::addParams() +JSFunction* FuncDeclNode::makeFunction(ExecState* exec, ScopeChainNode* scopeChain) { - for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam()) - m_body->parameters().append(p->ident()); + return new (exec) JSFunction(exec, m_ident, m_body.get(), scopeChain); } -FunctionImp* FuncDeclNode::makeFunction(ExecState* exec) +RegisterID* FuncDeclNode::emitCode(CodeGenerator&, RegisterID* dst) { - 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, ReadOnly | DontDelete | DontEnum); - func->putDirect(exec->propertyNames().prototype, proto, Internal | DontDelete); - func->putDirect(exec->propertyNames().length, jsNumber(m_body->parameters().size()), ReadOnly | DontDelete | DontEnum); - return func; -} - -JSValue* FuncDeclNode::execute(ExecState* exec) -{ - return exec->setNormalCompletion(); + return dst; } // ------------------------------ FuncExprNode --------------------------------- -// ECMA 13 -void FuncExprNode::addParams() +RegisterID* FuncExprNode::emitCode(CodeGenerator& generator, RegisterID* dst) { - for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam()) - m_body->parameters().append(p->ident()); + return generator.emitNewFunctionExpression(generator.finalDestination(dst), this); } -JSValue* FuncExprNode::evaluate(ExecState* exec) +JSFunction* FuncExprNode::makeFunction(ExecState* exec, ScopeChainNode* scopeChain) { - 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); - } + JSFunction* func = new (exec) JSFunction(exec, m_ident, m_body.get(), scopeChain); - 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, ReadOnly | DontDelete | DontEnum); - func->putDirect(exec->propertyNames().prototype, proto, Internal | DontDelete); + /* + The Identifier in a FunctionExpression can be referenced from inside + the FunctionExpression's FunctionBody to allow the function to call + itself recursively. However, unlike in a FunctionDeclaration, the + Identifier in a FunctionExpression cannot be referenced from and + does not affect the scope enclosing the FunctionExpression. + */ - if (named) { - functionScopeObject->putDirect(m_ident, func, Internal | ReadOnly | (exec->codeType() == EvalCode ? 0 : DontDelete)); - exec->popScope(); + if (!m_ident.isNull()) { + JSStaticScopeObject* functionScopeObject = new (exec) JSStaticScopeObject(exec, m_ident, func, ReadOnly | DontDelete); + func->scope().push(functionScopeObject); } return func; } -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/nodes.h b/JavaScriptCore/kjs/nodes.h index 11cf199..a70d350 100644 --- a/JavaScriptCore/kjs/nodes.h +++ b/JavaScriptCore/kjs/nodes.h @@ -26,28 +26,51 @@ #ifndef NODES_H_ #define NODES_H_ -#include "internal.h" -#include "regexp.h" +#include "Error.h" +#include "JSString.h" +#include "JSType.h" +#include "Opcode.h" +#include "RegisterID.h" +#include "ResultType.h" +#include "SourceCode.h" #include "SymbolTable.h" +#include "regexp.h" #include <wtf/ListRefPtr.h> #include <wtf/MathExtras.h> #include <wtf/OwnPtr.h> +#include <wtf/UnusedParam.h> #include <wtf/Vector.h> #if PLATFORM(X86) && COMPILER(GCC) -#define KJS_FAST_CALL __attribute__((regparm(3))) +#define JSC_FAST_CALL __attribute__((regparm(3))) #else -#define KJS_FAST_CALL +#define JSC_FAST_CALL #endif -namespace KJS { +namespace JSC { - class ConstDeclNode; + class CodeBlock; + class CodeGenerator; class FuncDeclNode; class Node; + class EvalCodeBlock; + class JSFunction; + class ProgramCodeBlock; class PropertyListNode; class SourceStream; + typedef unsigned int CodeFeatures; + + const CodeFeatures NoFeatures = 0; + const CodeFeatures EvalFeature = 1 << 0; + const CodeFeatures ClosureFeature = 1 << 1; + const CodeFeatures AssignFeature = 1 << 2; + const CodeFeatures ArgumentsFeature = 1 << 3; + const CodeFeatures WithFeature = 1 << 4; + const CodeFeatures CatchFeature = 1 << 5; + const CodeFeatures ThisFeature = 1 << 6; + const CodeFeatures AllFeatures = EvalFeature | ClosureFeature | AssignFeature | ArgumentsFeature | WithFeature | CatchFeature | ThisFeature; + enum Operator { OpEqual, OpPlusEq, @@ -62,7 +85,12 @@ namespace KJS { OpModEq, OpLShift, OpRShift, - OpURShift, + OpURShift + }; + + enum LogicalOperator { + OpLogicalAnd, + OpLogicalOr }; enum Precedence { @@ -72,7 +100,7 @@ namespace KJS { PrecLeftHandSide, PrecPostfix, PrecUnary, - PrecMultiplicitave, + PrecMultiplicative, PrecAdditive, PrecShift, PrecRelational, @@ -87,39 +115,31 @@ namespace KJS { PrecExpression }; - struct DeclarationStacks { + namespace DeclarationStacks { typedef Vector<Node*, 16> NodeStack; - enum { IsConstant = 1, HasInitializer = 2 } VarAttrs; + enum VarAttrs { IsConstant = 1, HasInitializer = 2 }; 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) - { - } + typedef Vector<RefPtr<FuncDeclNode>, 16> FunctionStack; + } - ExecState* exec; - NodeStack& nodeStack; - VarStack& varStack; - FunctionStack& functionStack; + struct SwitchInfo { + enum SwitchType { SwitchNone, SwitchImmediate, SwitchCharacter, SwitchString }; + uint32_t opcodeOffset; + SwitchType switchType; }; class ParserRefCounted : Noncopyable { protected: - ParserRefCounted() KJS_FAST_CALL; - ParserRefCounted(PlacementNewAdoptType) KJS_FAST_CALL - { - } + ParserRefCounted(JSGlobalData*) JSC_FAST_CALL; + + JSGlobalData* m_globalData; public: - void ref() KJS_FAST_CALL; - void deref() KJS_FAST_CALL; - unsigned refcount() KJS_FAST_CALL; + void ref() JSC_FAST_CALL; + void deref() JSC_FAST_CALL; + bool hasOneRef() JSC_FAST_CALL; - static void deleteNewObjects() KJS_FAST_CALL; + static void deleteNewObjects(JSGlobalData*) JSC_FAST_CALL; virtual ~ParserRefCounted(); }; @@ -130,96 +150,91 @@ namespace KJS { 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; } + Node(JSGlobalData*) JSC_FAST_CALL; - // 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 { } + /* + Return value: The register holding the production's value. + dst: An optional parameter specifying the most efficient + destination at which to store the production's value. + The callee must honor dst. - protected: - Node(JSType) KJS_FAST_CALL; // used by ExpressionNode + dst provides for a crude form of copy propagation. For example, - // 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; + x = 1 - // 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; + becomes + + load r[x], 1 + + instead of - JSValue* throwUndefinedVariableError(ExecState*, const Identifier&) KJS_FAST_CALL; + load r0, 1 + mov r[x], r0 + + because the assignment node, "x =", passes r[x] as dst to the number + node, "1". + */ + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* dst = 0) JSC_FAST_CALL + { + ASSERT_WITH_MESSAGE(0, "Don't know how to generate code for:\n%s\n", toString().ascii()); + UNUSED_PARAM(dst); + return 0; + } // FIXME: Make this pure virtual. - void handleException(ExecState*) KJS_FAST_CALL; - void handleException(ExecState*, JSValue*) KJS_FAST_CALL; + UString toString() const JSC_FAST_CALL; + int lineNo() const { return m_line; } - // for use in execute() - JSValue* rethrowException(ExecState*) KJS_FAST_CALL; + virtual bool isReturnNode() const JSC_FAST_CALL { return false; } - int m_line : 28; - unsigned m_expectedReturnType : 3; // JSType + // Serialization. + virtual void streamTo(SourceStream&) const JSC_FAST_CALL = 0; + virtual Precedence precedence() const = 0; + virtual bool needsParensIfLeftmost() const { return false; } + + protected: + int m_line; }; class ExpressionNode : public Node { public: - ExpressionNode() KJS_FAST_CALL : Node() {} - ExpressionNode(JSType expectedReturn) KJS_FAST_CALL - : Node(expectedReturn) + ExpressionNode(JSGlobalData* globalData, ResultType resultDesc = ResultType::unknown()) JSC_FAST_CALL + : Node(globalData) + , m_resultDesc(resultDesc) { } - // Special constructor for cases where we overwrite an object in place. - ExpressionNode(PlacementNewAdoptType) KJS_FAST_CALL - : Node(PlacementNewAdopt) - { - } + virtual bool isNumber() const JSC_FAST_CALL { return false; } + virtual bool isString() const JSC_FAST_CALL { return false; } + virtual bool isNull() const JSC_FAST_CALL { return false; } + virtual bool isPure(CodeGenerator&) const JSC_FAST_CALL { return false; } + virtual bool isLocation() const JSC_FAST_CALL { return false; } + virtual bool isResolveNode() const JSC_FAST_CALL { return false; } + virtual bool isBracketAccessorNode() const JSC_FAST_CALL { return false; } + virtual bool isDotAccessorNode() const JSC_FAST_CALL { return false; } - 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; } + virtual ExpressionNode* stripUnaryPlus() { return this; } - JSType expectedReturnType() const KJS_FAST_CALL { return static_cast<JSType>(m_expectedReturnType); } + ResultType resultDescriptor() const JSC_FAST_CALL { return m_resultDesc; } - 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; + // This needs to be in public in order to compile using GCC 3.x + typedef enum { EvalOperator, FunctionCall } CallerType; - // Used to optimize those nodes that do extra work when returning a result, even if the result has no semantic relevance - virtual void optimizeForUnnecessaryResult() { } + private: + ResultType m_resultDesc; }; 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; - void pushLabel(const Identifier& ident) KJS_FAST_CALL { m_labelStack.push(ident); } + StatementNode(JSGlobalData*) JSC_FAST_CALL; + void setLoc(int line0, int line1) JSC_FAST_CALL; + int firstLine() const JSC_FAST_CALL { return lineNo(); } + int lastLine() const JSC_FAST_CALL { return m_lastLine; } + virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } - virtual bool isEmptyStatement() const KJS_FAST_CALL { return false; } + virtual bool isEmptyStatement() const JSC_FAST_CALL { return false; } - protected: - LabelStack m_labelStack; + virtual bool isBlock() const JSC_FAST_CALL { return false; } + virtual bool isLoop() const JSC_FAST_CALL { return false; } private: int m_lastLine; @@ -227,68 +242,54 @@ namespace KJS { 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) + NullNode(JSGlobalData* globalData) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::nullType()) { } - 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; } - }; + virtual bool isNull() const JSC_FAST_CALL { return true; } - class TrueNode : public ExpressionNode { - public: - TrueNode() KJS_FAST_CALL - : ExpressionNode(BooleanType) - { - } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - 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 void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPrimary; } }; - class PlaceholderTrueNode : public TrueNode { + class BooleanNode : public ExpressionNode { public: - // Like TrueNode, but does not serialize as "true". - PlaceholderTrueNode() KJS_FAST_CALL - : TrueNode() + BooleanNode(JSGlobalData* globalData, bool value) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::boolean()) + , m_value(value) { } - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual bool isPure(CodeGenerator&) const JSC_FAST_CALL { return true; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + + protected: + bool m_value; }; class NumberNode : public ExpressionNode { public: - NumberNode(double v) KJS_FAST_CALL - : ExpressionNode(NumberType) + NumberNode(JSGlobalData* globalData, double v) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::constNumber()) , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_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; } + virtual bool isNumber() const JSC_FAST_CALL { return true; } + virtual bool isPure(CodeGenerator&) const JSC_FAST_CALL { return true; } + double value() const JSC_FAST_CALL { return m_double; } + virtual void setValue(double d) JSC_FAST_CALL { m_double = d; } protected: double m_double; @@ -296,18 +297,14 @@ namespace KJS { class ImmediateNumberNode : public NumberNode { public: - ImmediateNumberNode(JSValue* v, double d) KJS_FAST_CALL - : NumberNode(d) + ImmediateNumberNode(JSGlobalData* globalData, JSValue* v, double d) JSC_FAST_CALL + : NumberNode(globalData, 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); } + virtual void setValue(double d) JSC_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 @@ -315,127 +312,201 @@ namespace KJS { class StringNode : public ExpressionNode { public: - StringNode(const UString* v) KJS_FAST_CALL - : ExpressionNode(StringType) - , m_value(*v) + StringNode(JSGlobalData* globalData, const Identifier& v) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::string()) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual bool isString() const JSC_FAST_CALL { return true; } + const Identifier& value() { return m_value; } + virtual bool isPure(CodeGenerator&) const JSC_FAST_CALL { return true; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPrimary; } private: - UString m_value; + Identifier m_value; }; - - class RegExpNode : public ExpressionNode { + + class ThrowableExpressionData { public: - RegExpNode(const UString& pattern, const UString& flags) KJS_FAST_CALL - : m_regExp(new RegExp(pattern, flags)) + ThrowableExpressionData() + : m_divot(static_cast<uint32_t>(-1)) + , m_startOffset(static_cast<uint16_t>(-1)) + , m_endOffset(static_cast<uint16_t>(-1)) + { + } + + ThrowableExpressionData(unsigned divot, unsigned startOffset, unsigned endOffset) + : m_divot(divot) + , m_startOffset(startOffset) + , m_endOffset(endOffset) + { + } + + void setExceptionSourceCode(unsigned divot, unsigned startOffset, unsigned endOffset) { + m_divot = divot; + m_startOffset = startOffset; + m_endOffset = endOffset; } - JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; - virtual Precedence precedence() const { return PrecPrimary; } + uint32_t divot() const { return m_divot; } + uint16_t startOffset() const { return m_startOffset; } + uint16_t endOffset() const { return m_endOffset; } - private: - RefPtr<RegExp> m_regExp; + protected: + RegisterID* emitThrowError(CodeGenerator&, ErrorType, const char* msg); + RegisterID* emitThrowError(CodeGenerator&, ErrorType, const char* msg, const Identifier&); + uint32_t m_divot; + uint16_t m_startOffset; + uint16_t m_endOffset; }; - class ThisNode : public ExpressionNode { + class ThrowableSubExpressionData : public ThrowableExpressionData { public: - ThisNode() KJS_FAST_CALL + ThrowableSubExpressionData() + : ThrowableExpressionData() + , m_subexpressionDivotOffset(0) + , m_subexpressionEndOffset(0) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; - virtual Precedence precedence() const { return PrecPrimary; } - }; + ThrowableSubExpressionData(unsigned divot, unsigned startOffset, unsigned endOffset) + : ThrowableExpressionData(divot, startOffset, endOffset) + , m_subexpressionDivotOffset(0) + , m_subexpressionEndOffset(0) + { + } - class ResolveNode : public ExpressionNode { + void setSubexpressionInfo(uint32_t subexpressionDivot, uint16_t subexpressionOffset) { + ASSERT(subexpressionDivot <= m_divot); + if ((m_divot - subexpressionDivot) & ~0xFFFF) // Overflow means we can't do this safely, so just point at the primary divot + return; + m_subexpressionDivotOffset = m_divot - subexpressionDivot; + m_subexpressionEndOffset = subexpressionOffset; + } + + protected: + uint16_t m_subexpressionDivotOffset; + uint16_t m_subexpressionEndOffset; + }; + + class ThrowablePrefixedSubExpressionData : public ThrowableExpressionData { public: - ResolveNode(const Identifier& ident) KJS_FAST_CALL - : m_ident(ident) + ThrowablePrefixedSubExpressionData() + : ThrowableExpressionData() + , m_subexpressionDivotOffset(0) + , m_subexpressionStartOffset(0) { } - // Special constructor for cases where we overwrite an object in place. - ResolveNode(PlacementNewAdoptType) KJS_FAST_CALL - : ExpressionNode(PlacementNewAdopt) - , m_ident(PlacementNewAdopt) + ThrowablePrefixedSubExpressionData(unsigned divot, unsigned startOffset, unsigned endOffset) + : ThrowableExpressionData(divot, startOffset, endOffset) + , m_subexpressionDivotOffset(0) + , m_subexpressionStartOffset(0) + { + } + + void setSubexpressionInfo(uint32_t subexpressionDivot, uint16_t subexpressionOffset) { + ASSERT(subexpressionDivot >= m_divot); + if ((subexpressionDivot - m_divot) & ~0xFFFF) // Overflow means we can't do this safely, so just point at the primary divot + return; + m_subexpressionDivotOffset = subexpressionDivot - m_divot; + m_subexpressionStartOffset = subexpressionOffset; + } + + protected: + uint16_t m_subexpressionDivotOffset; + uint16_t m_subexpressionStartOffset; + }; + + class RegExpNode : public ExpressionNode, public ThrowableExpressionData { + public: + RegExpNode(JSGlobalData* globalData, const UString& pattern, const UString& flags) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_pattern(pattern) + , m_flags(flags) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_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 void streamTo(SourceStream&) const JSC_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; } + private: + UString m_pattern; + UString m_flags; + }; - protected: - ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); - Identifier m_ident; - size_t m_index; // Used by LocalVarAccessNode. + class ThisNode : public ExpressionNode { + public: + ThisNode(JSGlobalData* globalData) JSC_FAST_CALL + : ExpressionNode(globalData) + { + } + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } }; - class LocalVarAccessNode : public ResolveNode { + class ResolveNode : public ExpressionNode { public: - // Overwrites a ResolveNode in place. - LocalVarAccessNode(size_t i) KJS_FAST_CALL - : ResolveNode(PlacementNewAdopt) + ResolveNode(JSGlobalData* globalData, const Identifier& ident, int startOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_ident(ident) + , m_startOffset(startOffset) { - 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - private: - ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecPrimary; } + + virtual bool isPure(CodeGenerator&) const JSC_FAST_CALL; + virtual bool isLocation() const JSC_FAST_CALL { return true; } + virtual bool isResolveNode() const JSC_FAST_CALL { return true; } + const Identifier& identifier() const JSC_FAST_CALL { return m_ident; } + + protected: + Identifier m_ident; + int32_t m_startOffset; + }; class ElementNode : public Node { public: - ElementNode(int elision, ExpressionNode* node) KJS_FAST_CALL - : m_elision(elision) + ElementNode(JSGlobalData* globalData, int elision, ExpressionNode* node) JSC_FAST_CALL + : Node(globalData) + , m_elision(elision) , m_node(node) { } - ElementNode(ElementNode* l, int elision, ExpressionNode* node) KJS_FAST_CALL - : m_elision(elision) + ElementNode(JSGlobalData* globalData, ElementNode* l, int elision, ExpressionNode* node) JSC_FAST_CALL + : Node(globalData) + , 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; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; - PassRefPtr<ElementNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + int elision() const { return m_elision; } + ExpressionNode* value() { return m_node.get(); } - JSValue* evaluate(ExecState*) KJS_FAST_CALL; + ElementNode* next() { return m_next.get(); } + PassRefPtr<ElementNode> releaseNext() JSC_FAST_CALL { return m_next.release(); } private: - friend class ArrayNode; ListRefPtr<ElementNode> m_next; int m_elision; RefPtr<ExpressionNode> m_node; @@ -443,29 +514,32 @@ namespace KJS { class ArrayNode : public ExpressionNode { public: - ArrayNode(int elision) KJS_FAST_CALL - : m_elision(elision) + ArrayNode(JSGlobalData* globalData, int elision) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_elision(elision) , m_optional(true) { } - ArrayNode(ElementNode* element) KJS_FAST_CALL - : m_element(element) + ArrayNode(JSGlobalData* globalData, ElementNode* element) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_element(element) , m_elision(0) , m_optional(false) { } - ArrayNode(int elision, ElementNode* element) KJS_FAST_CALL - : m_element(element) + ArrayNode(JSGlobalData* globalData, int elision, ElementNode* element) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPrimary; } private: @@ -478,18 +552,17 @@ namespace KJS { public: enum Type { Constant, Getter, Setter }; - PropertyNode(const Identifier& name, ExpressionNode* assign, Type type) KJS_FAST_CALL - : m_name(name) + PropertyNode(JSGlobalData* globalData, const Identifier& name, ExpressionNode* assign, Type type) JSC_FAST_CALL + : Node(globalData) + , 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 void streamTo(SourceStream&) const JSC_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: @@ -501,23 +574,24 @@ namespace KJS { class PropertyListNode : public Node { public: - PropertyListNode(PropertyNode* node) KJS_FAST_CALL - : m_node(node) + PropertyListNode(JSGlobalData* globalData, PropertyNode* node) JSC_FAST_CALL + : Node(globalData) + , m_node(node) { } - PropertyListNode(PropertyNode* node, PropertyListNode* list) KJS_FAST_CALL - : m_node(node) + PropertyListNode(JSGlobalData* globalData, PropertyNode* node, PropertyListNode* list) JSC_FAST_CALL + : Node(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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(); } + PassRefPtr<PropertyListNode> releaseNext() JSC_FAST_CALL { return m_next.release(); } private: friend class ObjectLiteralNode; @@ -527,240 +601,215 @@ namespace KJS { class ObjectLiteralNode : public ExpressionNode { public: - ObjectLiteralNode() KJS_FAST_CALL + ObjectLiteralNode(JSGlobalData* globalData) JSC_FAST_CALL + : ExpressionNode(globalData) { } - ObjectLiteralNode(PropertyListNode* list) KJS_FAST_CALL - : m_list(list) + ObjectLiteralNode(JSGlobalData* globalData, PropertyListNode* list) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPrimary; } virtual bool needsParensIfLeftmost() const { return true; } private: RefPtr<PropertyListNode> m_list; }; - - class BracketAccessorNode : public ExpressionNode { + + class BracketAccessorNode : public ExpressionNode, public ThrowableExpressionData { public: - BracketAccessorNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL - : m_base(base) + BracketAccessorNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, bool subscriptHasAssignments) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_base(base) , m_subscript(subscript) + , m_subscriptHasAssignments(subscriptHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_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(); } + virtual bool isLocation() const JSC_FAST_CALL { return true; } + virtual bool isBracketAccessorNode() const JSC_FAST_CALL { return true; } + ExpressionNode* base() JSC_FAST_CALL { return m_base.get(); } + ExpressionNode* subscript() JSC_FAST_CALL { return m_subscript.get(); } private: - ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); - RefPtr<ExpressionNode> m_base; RefPtr<ExpressionNode> m_subscript; + bool m_subscriptHasAssignments; }; - class DotAccessorNode : public ExpressionNode { + class DotAccessorNode : public ExpressionNode, public ThrowableExpressionData { public: - DotAccessorNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL - : m_base(base) + DotAccessorNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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; } + virtual bool isLocation() const JSC_FAST_CALL { return true; } + virtual bool isDotAccessorNode() const JSC_FAST_CALL { return true; } + ExpressionNode* base() const JSC_FAST_CALL { return m_base.get(); } + const Identifier& identifier() const JSC_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(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : Node(globalData) + , m_expr(expr) { } - ArgumentListNode(ArgumentListNode* listNode, ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + ArgumentListNode(JSGlobalData* globalData, ArgumentListNode* listNode, ExpressionNode* expr) JSC_FAST_CALL + : Node(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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(); } + PassRefPtr<ArgumentListNode> releaseNext() JSC_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(JSGlobalData* globalData) JSC_FAST_CALL + : Node(globalData) { } - ArgumentsNode(ArgumentListNode* listNode) KJS_FAST_CALL - : m_listNode(listNode) + ArgumentsNode(JSGlobalData* globalData, ArgumentListNode* listNode) JSC_FAST_CALL + : Node(globalData) + , m_listNode(listNode) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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 { + class NewExprNode : public ExpressionNode, public ThrowableExpressionData { public: - NewExprNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + NewExprNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_expr(expr) { } - NewExprNode(ExpressionNode* expr, ArgumentsNode* args) KJS_FAST_CALL - : m_expr(expr) + NewExprNode(JSGlobalData* globalData, ExpressionNode* expr, ArgumentsNode* args) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_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 { + class EvalFunctionCallNode : public ExpressionNode, public ThrowableExpressionData { public: - FunctionCallValueNode(ExpressionNode* expr, ArgumentsNode* args) KJS_FAST_CALL - : m_expr(expr) + EvalFunctionCallNode(JSGlobalData* globalData, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecCall; } private: - RefPtr<ExpressionNode> m_expr; RefPtr<ArgumentsNode> m_args; }; - class FunctionCallResolveNode : public ExpressionNode { + class FunctionCallValueNode : public ExpressionNode, public ThrowableExpressionData { public: - FunctionCallResolveNode(const Identifier& ident, ArgumentsNode* args) KJS_FAST_CALL - : m_ident(ident) + FunctionCallValueNode(JSGlobalData* globalData, ExpressionNode* expr, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_expr(expr) , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecCall; } - protected: - ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); - - Identifier m_ident; + private: + RefPtr<ExpressionNode> m_expr; RefPtr<ArgumentsNode> m_args; - size_t m_index; // Used by LocalVarFunctionCallNode. }; - class LocalVarFunctionCallNode : public FunctionCallResolveNode { + class FunctionCallResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - LocalVarFunctionCallNode(size_t i) KJS_FAST_CALL - : FunctionCallResolveNode(PlacementNewAdopt) + FunctionCallResolveNode(JSGlobalData* globalData, const Identifier& ident, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_ident(ident) + , m_args(args) { - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - private: - ALWAYS_INLINE JSValue* inlineEvaluate(ExecState*); - }; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecCall; } - class FunctionCallBracketNode : public ExpressionNode { + protected: + Identifier m_ident; + RefPtr<ArgumentsNode> m_args; + size_t m_index; // Used by LocalVarFunctionCallNode. + size_t m_scopeDepth; // Used by ScopedVarFunctionCallNode and NonLocalVarFunctionCallNode + }; + + class FunctionCallBracketNode : public ExpressionNode, public ThrowableSubExpressionData { public: - FunctionCallBracketNode(ExpressionNode* base, ExpressionNode* subscript, ArgumentsNode* args) KJS_FAST_CALL - : m_base(base) + FunctionCallBracketNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecCall; } protected: @@ -769,232 +818,110 @@ namespace KJS { RefPtr<ArgumentsNode> m_args; }; - class FunctionCallDotNode : public ExpressionNode { + class FunctionCallDotNode : public ExpressionNode, public ThrowableSubExpressionData { public: - FunctionCallDotNode(ExpressionNode* base, const Identifier& ident, ArgumentsNode* args) KJS_FAST_CALL - : m_base(base) + FunctionCallDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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 { + class PrePostResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - PrePostResolveNode(const Identifier& ident) KJS_FAST_CALL - : ExpressionNode(NumberType) + PrePostResolveNode(JSGlobalData* globalData, const Identifier& ident, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::constNumber()) // could be reusable for pre? + , ThrowableExpressionData(divot, startOffset, endOffset) , 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 { + class PostfixResolveNode : public PrePostResolveNode { 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) + PostfixResolveNode(JSGlobalData* globalData, const Identifier& ident, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : PrePostResolveNode(globalData, ident, divot, startOffset, endOffset) + , m_operator(oper) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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; + protected: + Operator m_operator; }; - class PostfixBracketNode : public ExpressionNode { + class PostfixBracketNode : public ExpressionNode, public ThrowableSubExpressionData { public: - PostfixBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL - : m_base(base) + PostfixBracketNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_subscript(subscript) + , m_operator(oper) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPostfix; } protected: RefPtr<ExpressionNode> m_base; RefPtr<ExpressionNode> m_subscript; + Operator m_operator; }; - 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 { + class PostfixDotNode : public ExpressionNode, public ThrowableSubExpressionData { public: - PostfixDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL - : m_base(base) + PostfixDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_ident(ident) + , m_operator(oper) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPostfix; } protected: RefPtr<ExpressionNode> m_base; Identifier m_ident; + Operator m_operator; }; - 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 { + class PostfixErrorNode : public ExpressionNode, public ThrowableSubExpressionData { public: - PostfixErrorNode(ExpressionNode* expr, Operator oper) KJS_FAST_CALL - : m_expr(expr) + PostfixErrorNode(JSGlobalData* globalData, ExpressionNode* expr, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , m_expr(expr) , m_operator(oper) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPostfix; } private: @@ -1002,49 +929,37 @@ namespace KJS { Operator m_operator; }; - class DeleteResolveNode : public ExpressionNode { + class DeleteResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - DeleteResolveNode(const Identifier& ident) KJS_FAST_CALL - : m_ident(ident) + DeleteResolveNode(JSGlobalData* globalData, const Identifier& ident, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_ident(ident) { } - DeleteResolveNode(PlacementNewAdoptType) KJS_FAST_CALL - : ExpressionNode(PlacementNewAdopt) - , m_ident(PlacementNewAdopt) - { - } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - 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 void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } private: Identifier m_ident; }; - class LocalVarDeleteNode : public DeleteResolveNode { + class DeleteBracketNode : public ExpressionNode, public ThrowableExpressionData { 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) + DeleteBracketNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } private: @@ -1052,17 +967,19 @@ namespace KJS { RefPtr<ExpressionNode> m_subscript; }; - class DeleteDotNode : public ExpressionNode { + class DeleteDotNode : public ExpressionNode, public ThrowableExpressionData { public: - DeleteDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL - : m_base(base) + DeleteDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } private: @@ -1072,14 +989,15 @@ namespace KJS { class DeleteValueNode : public ExpressionNode { public: - DeleteValueNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + DeleteValueNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } private: @@ -1088,14 +1006,15 @@ namespace KJS { class VoidNode : public ExpressionNode { public: - VoidNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + VoidNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } private: @@ -1104,234 +1023,112 @@ namespace KJS { class TypeOfResolveNode : public ExpressionNode { public: - TypeOfResolveNode(const Identifier& ident) KJS_FAST_CALL - : ExpressionNode(StringType) + TypeOfResolveNode(JSGlobalData* globalData, const Identifier& ident) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::string()) , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } - const Identifier& identifier() const KJS_FAST_CALL { return m_ident; } + const Identifier& identifier() const JSC_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) + TypeOfValueNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::string()) , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_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 { + class PrefixResolveNode : public PrePostResolveNode { 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) + PrefixResolveNode(JSGlobalData* globalData, const Identifier& ident, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : PrePostResolveNode(globalData, ident, divot, startOffset, endOffset) + , m_operator(oper) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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; + protected: + Operator m_operator; }; - class PrefixBracketNode : public ExpressionNode { + class PrefixBracketNode : public ExpressionNode, public ThrowablePrefixedSubExpressionData { public: - PrefixBracketNode(ExpressionNode* base, ExpressionNode* subscript) KJS_FAST_CALL - : m_base(base) + PrefixBracketNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowablePrefixedSubExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_subscript(subscript) + , m_operator(oper) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } protected: RefPtr<ExpressionNode> m_base; RefPtr<ExpressionNode> m_subscript; + Operator m_operator; }; - 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 { + class PrefixDotNode : public ExpressionNode, public ThrowablePrefixedSubExpressionData { public: - PrefixDotNode(ExpressionNode* base, const Identifier& ident) KJS_FAST_CALL - : m_base(base) + PrefixDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowablePrefixedSubExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_ident(ident) + , m_operator(oper) { } - virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecPostfix; } protected: RefPtr<ExpressionNode> m_base; Identifier m_ident; + Operator m_operator; }; - 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 { + class PrefixErrorNode : public ExpressionNode, public ThrowableExpressionData { public: - PrefixErrorNode(ExpressionNode* expr, Operator oper) KJS_FAST_CALL - : m_expr(expr) + PrefixErrorNode(JSGlobalData* globalData, ExpressionNode* expr, Operator oper, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_expr(expr) , m_operator(oper) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } private: @@ -1339,693 +1136,421 @@ namespace KJS { Operator m_operator; }; - class UnaryPlusNode : public ExpressionNode { + class UnaryOpNode : public ExpressionNode { public: - UnaryPlusNode(ExpressionNode* expr) KJS_FAST_CALL - : ExpressionNode(NumberType) + UnaryOpNode(JSGlobalData* globalData, ExpressionNode* expr) + : ExpressionNode(globalData) , 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) + UnaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr) + : ExpressionNode(globalData, type) , 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; } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL = 0; - private: + protected: RefPtr<ExpressionNode> m_expr; }; - class BitwiseNotNode : public ExpressionNode { + class UnaryPlusNode : public UnaryOpNode { public: - BitwiseNotNode(ExpressionNode* expr) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_expr(expr) + UnaryPlusNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : UnaryOpNode(globalData, ResultType::constNumber(), 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*); + virtual ExpressionNode* stripUnaryPlus() { return m_expr.get(); } - RefPtr<ExpressionNode> m_expr; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_to_jsnumber; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } }; - class LogicalNotNode : public ExpressionNode { + class NegateNode : public UnaryOpNode { public: - LogicalNotNode(ExpressionNode* expr) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr(expr) + NegateNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : UnaryOpNode(globalData, ResultType::reusableNumber(), 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 OpcodeID opcode() const JSC_FAST_CALL { return op_negate; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecUnary; } - - private: - RefPtr<ExpressionNode> m_expr; }; - class MultNode : public ExpressionNode { + class BitwiseNotNode : public UnaryOpNode { public: - MultNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + BitwiseNotNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : UnaryOpNode(globalData, ResultType::reusableNumber(), 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 PrecMultiplicitave; } - - private: - ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); - - RefPtr<ExpressionNode> m_term1; - RefPtr<ExpressionNode> m_term2; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_bitnot; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } }; - class DivNode : public ExpressionNode { + class LogicalNotNode : public UnaryOpNode { public: - DivNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + LogicalNotNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : UnaryOpNode(globalData, ResultType::boolean(), 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 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; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_not; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecUnary; } }; - class ModNode : public ExpressionNode { + class BinaryOpNode : public ExpressionNode { public: - ModNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + BinaryOpNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : ExpressionNode(globalData) + , m_expr1(expr1) + , m_expr2(expr2) + , m_rightHasAssignments(rightHasAssignments) { } - 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; } + BinaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : ExpressionNode(globalData, type) + , m_expr1(expr1) + , m_expr2(expr2) + , m_rightHasAssignments(rightHasAssignments) + { + } - private: - ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL = 0; - RefPtr<ExpressionNode> m_term1; - RefPtr<ExpressionNode> m_term2; + protected: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + bool m_rightHasAssignments; }; - class AddNode : public ExpressionNode { + class ReverseBinaryOpNode : public ExpressionNode { public: - AddNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : m_term1(term1) - , m_term2(term2) + ReverseBinaryOpNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : ExpressionNode(globalData) + , m_expr1(expr1) + , m_expr2(expr2) + , m_rightHasAssignments(rightHasAssignments) { } - 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) + ReverseBinaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : ExpressionNode(globalData, type) + , m_expr1(expr1) + , m_expr2(expr2) + , m_rightHasAssignments(rightHasAssignments) { } - RefPtr<ExpressionNode> m_term1; - RefPtr<ExpressionNode> m_term2; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL = 0; - private: - ALWAYS_INLINE double inlineEvaluateToNumber(ExecState*); + protected: + RefPtr<ExpressionNode> m_expr1; + RefPtr<ExpressionNode> m_expr2; + bool m_rightHasAssignments; }; - class AddNumbersNode : public AddNode { + class MultNode : public BinaryOpNode { public: - AddNumbersNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : AddNode(term1, term2, NumberType) + MultNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_mul; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecMultiplicative; } }; - class AddStringLeftNode : public AddNode { + class DivNode : public BinaryOpNode { public: - AddStringLeftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : AddNode(term1, term2, StringType) + DivNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_div; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecMultiplicative; } }; - class AddStringRightNode : public AddNode { + class ModNode : public BinaryOpNode { public: - AddStringRightNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : AddNode(term1, term2, StringType) + ModNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_mod; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecMultiplicative; } }; - class AddStringsNode : public AddNode { + class AddNode : public BinaryOpNode { public: - AddStringsNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : AddNode(term1, term2, StringType) + AddNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::forAdd(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, rightHasAssignments) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_add; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecAdditive; } }; - class SubNode : public ExpressionNode { + class SubNode : public BinaryOpNode { public: - SubNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + SubNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_sub; } + virtual void streamTo(SourceStream&) const JSC_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 { + class LeftShiftNode : public BinaryOpNode { public: - LeftShiftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + LeftShiftNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_lshift; } + virtual void streamTo(SourceStream&) const JSC_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 { + class RightShiftNode : public BinaryOpNode { public: - RightShiftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + RightShiftNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_rshift; } + virtual void streamTo(SourceStream&) const JSC_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 { + class UnsignedRightShiftNode : public BinaryOpNode { public: - UnsignedRightShiftNode(ExpressionNode* term1, ExpressionNode* term2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_term1(term1) - , m_term2(term2) + UnsignedRightShiftNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_urshift; } + virtual void streamTo(SourceStream&) const JSC_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 { + class LessNode : public BinaryOpNode { public: - LessNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr1(expr1) - , m_expr2(expr2) + LessNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_less; } + virtual void streamTo(SourceStream&) const JSC_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 { + class GreaterNode : public ReverseBinaryOpNode { public: - LessNumbersNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : LessNode(expr1, expr2) + GreaterNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : ReverseBinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; - - private: - ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_less; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } }; - class LessStringsNode : public LessNode { + class LessEqNode : public BinaryOpNode { public: - LessStringsNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : LessNode(expr1, expr2) + LessEqNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual bool evaluateToBoolean(ExecState*) KJS_FAST_CALL; - - private: - ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_lesseq; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return PrecRelational; } }; - class GreaterNode : public ExpressionNode { + class GreaterEqNode : public ReverseBinaryOpNode { public: - GreaterNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : m_expr1(expr1) - , m_expr2(expr2) + GreaterEqNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : ReverseBinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_lesseq; } + virtual void streamTo(SourceStream&) const JSC_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 { + class ThrowableBinaryOpNode : public BinaryOpNode, public ThrowableExpressionData { public: - LessEqNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : m_expr1(expr1) - , m_expr2(expr2) + ThrowableBinaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, type, expr1, expr2, rightHasAssignments) { } - - 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) + ThrowableBinaryOpNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, expr1, expr2, rightHasAssignments) { } - - 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; }; - - class InstanceOfNode : public ExpressionNode { + + class InstanceOfNode : public ThrowableBinaryOpNode { public: - InstanceOfNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr1(expr1) - , m_expr2(expr2) + InstanceOfNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : ThrowableBinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_instanceof; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecRelational; } - private: - RefPtr<ExpressionNode> m_expr1; - RefPtr<ExpressionNode> m_expr2; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; }; - class InNode : public ExpressionNode { + class InNode : public ThrowableBinaryOpNode { public: - InNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : m_expr1(expr1) - , m_expr2(expr2) + InNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : ThrowableBinaryOpNode(globalData, expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_in; } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecRelational; } - - private: - RefPtr<ExpressionNode> m_expr1; - RefPtr<ExpressionNode> m_expr2; }; - class EqualNode : public ExpressionNode { + class EqualNode : public BinaryOpNode { public: - EqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr1(expr1) - , m_expr2(expr2) + EqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_eq; } + virtual void streamTo(SourceStream&) const JSC_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 { + class NotEqualNode : public BinaryOpNode { public: - NotEqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr1(expr1) - , m_expr2(expr2) + NotEqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_neq; } + virtual void streamTo(SourceStream&) const JSC_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 { + class StrictEqualNode : public BinaryOpNode { public: - StrictEqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr1(expr1) - , m_expr2(expr2) + StrictEqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual OpcodeID opcode() const JSC_FAST_CALL { return op_stricteq; } + virtual void streamTo(SourceStream&) const JSC_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 { + class NotStrictEqualNode : public BinaryOpNode { public: - NotStrictEqualNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) - , m_expr1(expr1) - , m_expr2(expr2) + NotStrictEqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_nstricteq; } + virtual void streamTo(SourceStream&) const JSC_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 { + class BitAndNode : public BinaryOpNode { public: - BitAndNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_expr1(expr1) - , m_expr2(expr2) + BitAndNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_bitand; } + virtual void streamTo(SourceStream&) const JSC_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 { + class BitOrNode : public BinaryOpNode { public: - BitOrNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_expr1(expr1) - , m_expr2(expr2) + BitOrNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_bitor; } + virtual void streamTo(SourceStream&) const JSC_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 { + class BitXOrNode : public BinaryOpNode { public: - BitXOrNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(NumberType) - , m_expr1(expr1) - , m_expr2(expr2) + BitXOrNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL + : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments) { } - 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 OpcodeID opcode() const JSC_FAST_CALL { return op_bitxor; } + virtual void streamTo(SourceStream&) const JSC_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 { + class LogicalOpNode : public ExpressionNode { public: - LogicalOrNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : ExpressionNode(BooleanType) + LogicalOpNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator oper) JSC_FAST_CALL + : ExpressionNode(globalData, ResultType::boolean()) , m_expr1(expr1) , m_expr2(expr2) + , m_operator(oper) { } - 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; } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual Precedence precedence() const { return (m_operator == OpLogicalAnd) ? PrecLogicalAnd : PrecLogicalOr; } private: - ALWAYS_INLINE bool inlineEvaluateToBoolean(ExecState*); - RefPtr<ExpressionNode> m_expr1; RefPtr<ExpressionNode> m_expr2; + LogicalOperator m_operator; }; /** @@ -2033,20 +1558,16 @@ namespace KJS { */ class ConditionalNode : public ExpressionNode { public: - ConditionalNode(ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : m_logical(logical) + ConditionalNode(JSGlobalData* globalData, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecConditional; } private: @@ -2055,201 +1576,169 @@ namespace KJS { RefPtr<ExpressionNode> m_expr2; }; - class ReadModifyResolveNode : public ExpressionNode { + class ReadModifyResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - ReadModifyResolveNode(const Identifier& ident, Operator oper, ExpressionNode* right) KJS_FAST_CALL - : m_ident(ident) - , m_operator(oper) + ReadModifyResolveNode(JSGlobalData* globalData, const Identifier& ident, Operator oper, ExpressionNode* right, bool rightHasAssignments, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_ident(ident) , m_right(right) + , m_operator(oper) + , m_rightHasAssignments(rightHasAssignments) { } - ReadModifyResolveNode(PlacementNewAdoptType) KJS_FAST_CALL - : ExpressionNode(PlacementNewAdopt) - , m_ident(PlacementNewAdopt) - , m_right(PlacementNewAdopt) - { - } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - 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 void streamTo(SourceStream&) const JSC_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. + Operator m_operator : 31; + bool m_rightHasAssignments : 1; }; - 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 { + class AssignResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - AssignResolveNode(const Identifier& ident, ExpressionNode* right) KJS_FAST_CALL - : m_ident(ident) + AssignResolveNode(JSGlobalData* globalData, const Identifier& ident, ExpressionNode* right, bool rightHasAssignments) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_ident(ident) , m_right(right) + , m_rightHasAssignments(rightHasAssignments) { } - AssignResolveNode(PlacementNewAdoptType) KJS_FAST_CALL - : ExpressionNode(PlacementNewAdopt) - , m_ident(PlacementNewAdopt) - , m_right(PlacementNewAdopt) - { - } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; - 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 void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecAssignment; } protected: Identifier m_ident; RefPtr<ExpressionNode> m_right; size_t m_index; // Used by ReadModifyLocalVarNode. + bool m_rightHasAssignments; }; - 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 { + class ReadModifyBracketNode : public ExpressionNode, public ThrowableSubExpressionData { public: - ReadModifyBracketNode(ExpressionNode* base, ExpressionNode* subscript, Operator oper, ExpressionNode* right) KJS_FAST_CALL - : m_base(base) + ReadModifyBracketNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, Operator oper, ExpressionNode* right, bool subscriptHasAssignments, bool rightHasAssignments, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_subscript(subscript) - , m_operator(oper) , m_right(right) + , m_operator(oper) + , m_subscriptHasAssignments(subscriptHasAssignments) + , m_rightHasAssignments(rightHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecAssignment; } protected: RefPtr<ExpressionNode> m_base; RefPtr<ExpressionNode> m_subscript; - Operator m_operator; RefPtr<ExpressionNode> m_right; + Operator m_operator : 30; + bool m_subscriptHasAssignments : 1; + bool m_rightHasAssignments : 1; }; - class AssignBracketNode : public ExpressionNode { + class AssignBracketNode : public ExpressionNode, public ThrowableExpressionData { public: - AssignBracketNode(ExpressionNode* base, ExpressionNode* subscript, ExpressionNode* right) KJS_FAST_CALL - : m_base(base) + AssignBracketNode(JSGlobalData* globalData, ExpressionNode* base, ExpressionNode* subscript, ExpressionNode* right, bool subscriptHasAssignments, bool rightHasAssignments, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_subscript(subscript) , m_right(right) + , m_subscriptHasAssignments(subscriptHasAssignments) + , m_rightHasAssignments(rightHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecAssignment; } protected: RefPtr<ExpressionNode> m_base; RefPtr<ExpressionNode> m_subscript; RefPtr<ExpressionNode> m_right; + bool m_subscriptHasAssignments : 1; + bool m_rightHasAssignments : 1; }; - class AssignDotNode : public ExpressionNode { + class AssignDotNode : public ExpressionNode, public ThrowableExpressionData { public: - AssignDotNode(ExpressionNode* base, const Identifier& ident, ExpressionNode* right) KJS_FAST_CALL - : m_base(base) + AssignDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, ExpressionNode* right, bool rightHasAssignments, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_ident(ident) , m_right(right) + , m_rightHasAssignments(rightHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecAssignment; } protected: RefPtr<ExpressionNode> m_base; Identifier m_ident; RefPtr<ExpressionNode> m_right; + bool m_rightHasAssignments; }; - class ReadModifyDotNode : public ExpressionNode { + class ReadModifyDotNode : public ExpressionNode, public ThrowableSubExpressionData { public: - ReadModifyDotNode(ExpressionNode* base, const Identifier& ident, Operator oper, ExpressionNode* right) KJS_FAST_CALL - : m_base(base) + ReadModifyDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, Operator oper, ExpressionNode* right, bool rightHasAssignments, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableSubExpressionData(divot, startOffset, endOffset) + , m_base(base) , m_ident(ident) - , m_operator(oper) , m_right(right) + , m_operator(oper) + , m_rightHasAssignments(rightHasAssignments) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecAssignment; } protected: RefPtr<ExpressionNode> m_base; Identifier m_ident; - Operator m_operator; RefPtr<ExpressionNode> m_right; + Operator m_operator : 31; + bool m_rightHasAssignments : 1; }; - class AssignErrorNode : public ExpressionNode { + class AssignErrorNode : public ExpressionNode, public ThrowableExpressionData { public: - AssignErrorNode(ExpressionNode* left, Operator oper, ExpressionNode* right) KJS_FAST_CALL - : m_left(left) + AssignErrorNode(JSGlobalData* globalData, ExpressionNode* left, Operator oper, ExpressionNode* right, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL + : ExpressionNode(globalData) + , ThrowableExpressionData(divot, startOffset, endOffset) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecAssignment; } protected: @@ -2260,16 +1749,15 @@ namespace KJS { class CommaNode : public ExpressionNode { public: - CommaNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : m_expr1(expr1) + CommaNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2) JSC_FAST_CALL + : ExpressionNode(globalData) + , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecExpression; } private: @@ -2279,8 +1767,8 @@ namespace KJS { class VarDeclCommaNode : public CommaNode { public: - VarDeclCommaNode(ExpressionNode* expr1, ExpressionNode* expr2) KJS_FAST_CALL - : CommaNode(expr1, expr2) + VarDeclCommaNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2) JSC_FAST_CALL + : CommaNode(globalData, expr1, expr2) { } virtual Precedence precedence() const { return PrecAssignment; } @@ -2288,33 +1776,31 @@ namespace KJS { class ConstDeclNode : public ExpressionNode { public: - ConstDeclNode(const Identifier& ident, ExpressionNode* in) KJS_FAST_CALL; + ConstDeclNode(JSGlobalData* globalData, const Identifier& ident, ExpressionNode* in) JSC_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 void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } - PassRefPtr<ConstDeclNode> releaseNext() KJS_FAST_CALL { return m_next.release(); } + PassRefPtr<ConstDeclNode> releaseNext() JSC_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; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual RegisterID* emitCodeSingle(CodeGenerator&) JSC_FAST_CALL; }; class ConstStatementNode : public StatementNode { public: - ConstStatementNode(ConstDeclNode* next) KJS_FAST_CALL - : m_next(next) + ConstStatementNode(JSGlobalData* globalData, ConstDeclNode* next) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; private: RefPtr<ConstDeclNode> m_next; @@ -2324,6 +1810,8 @@ namespace KJS { class SourceElements : public ParserRefCounted { public: + SourceElements(JSGlobalData* globalData) : ParserRefCounted(globalData) {} + void append(PassRefPtr<StatementNode>); void releaseContentsIntoVector(StatementVector& destination) { @@ -2337,37 +1825,53 @@ namespace KJS { class BlockNode : public StatementNode { public: - BlockNode(SourceElements* children) KJS_FAST_CALL; + BlockNode(JSGlobalData*, SourceElements* children) JSC_FAST_CALL; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_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; + StatementVector& children() { return m_children; } + virtual bool isBlock() const JSC_FAST_CALL { return true; } protected: StatementVector m_children; }; class EmptyStatementNode : public StatementNode { public: - EmptyStatementNode() KJS_FAST_CALL // debug + EmptyStatementNode(JSGlobalData* globalData) JSC_FAST_CALL // debug + : StatementNode(globalData) { } - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; - virtual bool isEmptyStatement() const KJS_FAST_CALL { return true; } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual bool isEmptyStatement() const JSC_FAST_CALL { return true; } + }; + + class DebuggerStatementNode : public StatementNode { + public: + DebuggerStatementNode(JSGlobalData* globalData) JSC_FAST_CALL + : StatementNode(globalData) + { + } + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; }; class ExprStatementNode : public StatementNode { public: - ExprStatementNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + ExprStatementNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: RefPtr<ExpressionNode> m_expr; @@ -2375,14 +1879,15 @@ namespace KJS { class VarStatementNode : public StatementNode { public: - VarStatementNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + VarStatementNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : StatementNode(globalData) + , m_expr(expr) { } + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_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; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: RefPtr<ExpressionNode> m_expr; @@ -2390,15 +1895,15 @@ namespace KJS { class IfNode : public StatementNode { public: - IfNode(ExpressionNode* condition, StatementNode* ifBlock) KJS_FAST_CALL - : m_condition(condition) + IfNode(JSGlobalData* globalData, ExpressionNode* condition, StatementNode* ifBlock) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; protected: RefPtr<ExpressionNode> m_condition; @@ -2407,15 +1912,14 @@ namespace KJS { class IfElseNode : public IfNode { public: - IfElseNode(ExpressionNode* condtion, StatementNode* ifBlock, StatementNode* elseBlock) KJS_FAST_CALL - : IfNode(condtion, ifBlock) + IfElseNode(JSGlobalData* globalData, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock) JSC_FAST_CALL + : IfNode(globalData, condition, 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: RefPtr<StatementNode> m_elseBlock; @@ -2423,16 +1927,17 @@ namespace KJS { class DoWhileNode : public StatementNode { public: - DoWhileNode(StatementNode* statement, ExpressionNode* expr) KJS_FAST_CALL - : m_statement(statement) + DoWhileNode(JSGlobalData* globalData, StatementNode* statement, ExpressionNode* expr) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual bool isLoop() const JSC_FAST_CALL { return true; } private: RefPtr<StatementNode> m_statement; RefPtr<ExpressionNode> m_expr; @@ -2440,16 +1945,17 @@ namespace KJS { class WhileNode : public StatementNode { public: - WhileNode(ExpressionNode* expr, StatementNode* statement) KJS_FAST_CALL - : m_expr(expr) + WhileNode(JSGlobalData* globalData, ExpressionNode* expr, StatementNode* statement) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual bool isLoop() const JSC_FAST_CALL { return true; } private: RefPtr<ExpressionNode> m_expr; RefPtr<StatementNode> m_statement; @@ -2457,26 +1963,21 @@ namespace KJS { 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) + ForNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode* statement, bool expr1WasVarDecl) JSC_FAST_CALL + : StatementNode(globalData) + , m_expr1(expr1) + , m_expr2(expr2) + , m_expr3(expr3) , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual bool isLoop() const JSC_FAST_CALL { return true; } private: RefPtr<ExpressionNode> m_expr1; RefPtr<ExpressionNode> m_expr2; @@ -2485,15 +1986,15 @@ namespace KJS { bool m_expr1WasVarDecl; }; - class ForInNode : public StatementNode { + class ForInNode : public StatementNode, public ThrowableExpressionData { 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; + ForInNode(JSGlobalData*, ExpressionNode*, ExpressionNode*, StatementNode*) JSC_FAST_CALL; + ForInNode(JSGlobalData*, const Identifier&, ExpressionNode*, ExpressionNode*, StatementNode*, int divot, int startOffset, int endOffset) JSC_FAST_CALL; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual bool isLoop() const JSC_FAST_CALL { return true; } private: Identifier m_ident; RefPtr<ExpressionNode> m_init; @@ -2503,52 +2004,57 @@ namespace KJS { bool m_identIsVarDecl; }; - class ContinueNode : public StatementNode { + class ContinueNode : public StatementNode, public ThrowableExpressionData { public: - ContinueNode() KJS_FAST_CALL + ContinueNode(JSGlobalData* globalData) JSC_FAST_CALL + : StatementNode(globalData) { } - ContinueNode(const Identifier& ident) KJS_FAST_CALL - : m_ident(ident) + ContinueNode(JSGlobalData* globalData, const Identifier& ident) JSC_FAST_CALL + : StatementNode(globalData) + , m_ident(ident) { } - - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: Identifier m_ident; }; - class BreakNode : public StatementNode { + class BreakNode : public StatementNode, public ThrowableExpressionData { public: - BreakNode() KJS_FAST_CALL + BreakNode(JSGlobalData* globalData) JSC_FAST_CALL + : StatementNode(globalData) { } - BreakNode(const Identifier& ident) KJS_FAST_CALL - : m_ident(ident) + BreakNode(JSGlobalData* globalData, const Identifier& ident) JSC_FAST_CALL + : StatementNode(globalData) + , m_ident(ident) { } - - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: Identifier m_ident; }; - class ReturnNode : public StatementNode { + class ReturnNode : public StatementNode, public ThrowableExpressionData { public: - ReturnNode(ExpressionNode* value) KJS_FAST_CALL - : m_value(value) + ReturnNode(JSGlobalData* globalData, ExpressionNode* value) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + virtual bool isReturnNode() const JSC_FAST_CALL { return true; } private: RefPtr<ExpressionNode> m_value; @@ -2556,48 +2062,52 @@ namespace KJS { class WithNode : public StatementNode { public: - WithNode(ExpressionNode* expr, StatementNode* statement) KJS_FAST_CALL - : m_expr(expr) + WithNode(JSGlobalData* globalData, ExpressionNode* expr, StatementNode* statement, uint32_t divot, uint32_t expressionLength) JSC_FAST_CALL + : StatementNode(globalData) + , m_expr(expr) , m_statement(statement) + , m_divot(divot) + , m_expressionLength(expressionLength) { } - 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: RefPtr<ExpressionNode> m_expr; RefPtr<StatementNode> m_statement; + uint32_t m_divot; + uint32_t m_expressionLength; }; - class LabelNode : public StatementNode { + class LabelNode : public StatementNode, public ThrowableExpressionData { public: - LabelNode(const Identifier& label, StatementNode* statement) KJS_FAST_CALL - : m_label(label) + LabelNode(JSGlobalData* globalData, const Identifier& name, StatementNode* statement) JSC_FAST_CALL + : StatementNode(globalData) + , m_name(name) , 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 RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: - Identifier m_label; + Identifier m_name; RefPtr<StatementNode> m_statement; }; - class ThrowNode : public StatementNode { + class ThrowNode : public StatementNode, public ThrowableExpressionData { public: - ThrowNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + ThrowNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; private: RefPtr<ExpressionNode> m_expr; @@ -2605,17 +2115,18 @@ namespace KJS { class TryNode : public StatementNode { public: - TryNode(StatementNode* tryBlock, const Identifier& exceptionIdent, StatementNode* catchBlock, StatementNode* finallyBlock) KJS_FAST_CALL - : m_tryBlock(tryBlock) + TryNode(JSGlobalData* globalData, StatementNode* tryBlock, const Identifier& exceptionIdent, StatementNode* catchBlock, StatementNode* finallyBlock) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* dst = 0) JSC_FAST_CALL; private: RefPtr<StatementNode> m_tryBlock; @@ -2626,21 +2137,23 @@ namespace KJS { class ParameterNode : public Node { public: - ParameterNode(const Identifier& ident) KJS_FAST_CALL - : m_ident(ident) + ParameterNode(JSGlobalData* globalData, const Identifier& ident) JSC_FAST_CALL + : Node(globalData) + , m_ident(ident) { } - ParameterNode(ParameterNode* l, const Identifier& ident) KJS_FAST_CALL - : m_ident(ident) + ParameterNode(JSGlobalData* globalData, ParameterNode* l, const Identifier& ident) JSC_FAST_CALL + : Node(globalData) + , 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(); } + Identifier ident() JSC_FAST_CALL { return m_ident; } + ParameterNode *nextParam() JSC_FAST_CALL { return m_next.get(); } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + PassRefPtr<ParameterNode> releaseNext() JSC_FAST_CALL { return m_next.release(); } virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } private: @@ -2652,92 +2165,173 @@ namespace KJS { class ScopeNode : public BlockNode { public: - ScopeNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + ScopeNode(JSGlobalData*, const SourceCode&, SourceElements*, VarStack*, FunctionStack*, CodeFeatures, int numConstants) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + + const SourceCode& source() const { return m_source; } + const UString& sourceURL() const JSC_FAST_CALL { return m_source.provider()->url(); } + intptr_t sourceID() const { return m_source.provider()->asID(); } - 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; + bool usesEval() const { return m_features & EvalFeature; } + bool usesArguments() const { return m_features & ArgumentsFeature; } + void setUsesArguments() { m_features |= ArgumentsFeature; } + bool usesThis() const { return m_features & ThisFeature; } + bool needsActivation() const { return m_features & (EvalFeature | ClosureFeature | WithFeature | CatchFeature); } + + VarStack& varStack() { return m_varStack; } + FunctionStack& functionStack() { return m_functionStack; } + + int neededConstants() + { + // We may need 1 more constant than the count given by the parser, + // because of the various uses of jsUndefined(). + return m_numConstants + 1; + } protected: - void optimizeVariableAccess(ExecState*) KJS_FAST_CALL; + void setSource(const SourceCode& source) { m_source = source; } VarStack m_varStack; FunctionStack m_functionStack; private: - UString m_sourceURL; - int m_sourceId; + SourceCode m_source; + CodeFeatures m_features; + int m_numConstants; }; class ProgramNode : public ScopeNode { public: - static ProgramNode* create(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + static ProgramNode* create(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, const SourceCode&, CodeFeatures, int numConstants) JSC_FAST_CALL; - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + ProgramCodeBlock& byteCode(ScopeChainNode* scopeChain) JSC_FAST_CALL + { + if (!m_code) + generateCode(scopeChain); + return *m_code; + } private: - ProgramNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + ProgramNode(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, const SourceCode&, CodeFeatures, int numConstants) JSC_FAST_CALL; - void initializeSymbolTable(ExecState*) KJS_FAST_CALL; - ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL; + void generateCode(ScopeChainNode*) JSC_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_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.) + + OwnPtr<ProgramCodeBlock> m_code; }; class EvalNode : public ScopeNode { public: - static EvalNode* create(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + static EvalNode* create(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, const SourceCode&, CodeFeatures, int numConstants) JSC_FAST_CALL; - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + EvalCodeBlock& byteCode(ScopeChainNode* scopeChain) JSC_FAST_CALL + { + if (!m_code) + generateCode(scopeChain); + return *m_code; + } private: - EvalNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + EvalNode(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, const SourceCode&, CodeFeatures, int numConstants) JSC_FAST_CALL; - ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL; + void generateCode(ScopeChainNode*) JSC_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + OwnPtr<EvalCodeBlock> m_code; }; class FunctionBodyNode : public ScopeNode { public: - static FunctionBodyNode* create(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + static FunctionBodyNode* create(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, const SourceCode&, CodeFeatures, int numConstants) JSC_FAST_CALL; + static FunctionBodyNode* create(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, CodeFeatures, int numConstants) JSC_FAST_CALL; + ~FunctionBodyNode(); - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; + const Identifier* parameters() const JSC_FAST_CALL { return m_parameters; } + size_t parameterCount() const { return m_parameterCount; } + UString paramString() const JSC_FAST_CALL; + Identifier* copyParameters(); - SymbolTable& symbolTable() KJS_FAST_CALL { return m_symbolTable; } + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + SymbolTable& symbolTable() { return m_symbolTable; } // FIXME: Remove this + + CodeBlock& byteCode(ScopeChainNode* scopeChain) JSC_FAST_CALL + { + ASSERT(scopeChain); + if (!m_code) + generateCode(scopeChain); + return *m_code; + } - Vector<Identifier>& parameters() KJS_FAST_CALL { return m_parameters; } - UString paramString() const KJS_FAST_CALL; + CodeBlock& generatedByteCode() JSC_FAST_CALL + { + ASSERT(m_code); + return *m_code; + } + + bool isGenerated() JSC_FAST_CALL + { + return m_code; + } + + void mark(); + + void finishParsing(const SourceCode&, ParameterNode*); + void finishParsing(Identifier* parameters, size_t parameterCount); + + UString toSourceString() const JSC_FAST_CALL { return UString("{") + source().toString() + UString("}"); } + + // These objects are ref/deref'd a lot in the scope chain, so this is a faster ref/deref. + // If the virtual machine changes so this doesn't happen as much we can change back. + void ref() + { + if (++m_refCount == 1) + ScopeNode::ref(); + } + void deref() + { + ASSERT(m_refCount); + if (!--m_refCount) + ScopeNode::deref(); + } protected: - FunctionBodyNode(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; + FunctionBodyNode(JSGlobalData*, SourceElements*, VarStack*, FunctionStack*, const SourceCode&, CodeFeatures, int numConstants) JSC_FAST_CALL; private: - void initializeSymbolTable(ExecState*) KJS_FAST_CALL; - ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL; + void generateCode(ScopeChainNode*) JSC_FAST_CALL; - bool m_initialized; - Vector<Identifier> m_parameters; + Identifier* m_parameters; + size_t m_parameterCount; SymbolTable m_symbolTable; + OwnPtr<CodeBlock> m_code; + unsigned m_refCount; }; class FuncExprNode : public ExpressionNode { public: - FuncExprNode(const Identifier& ident, FunctionBodyNode* body, ParameterNode* parameter = 0) KJS_FAST_CALL - : m_ident(ident) + FuncExprNode(JSGlobalData* globalData, const Identifier& ident, FunctionBodyNode* body, const SourceCode& source, ParameterNode* parameter = 0) JSC_FAST_CALL + : ExpressionNode(globalData) + , m_ident(ident) , m_parameter(parameter) , m_body(body) { - addParams(); + m_body->finishParsing(source, m_parameter.get()); } - virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + JSFunction* makeFunction(ExecState*, ScopeChainNode*) JSC_FAST_CALL; + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { return PrecMember; } virtual bool needsParensIfLeftmost() const { return true; } - private: - void addParams() KJS_FAST_CALL; + FunctionBodyNode* body() { return m_body.get(); } + private: // Used for streamTo friend class PropertyNode; Identifier m_ident; @@ -2747,54 +2341,50 @@ namespace KJS { 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) + FuncDeclNode(JSGlobalData* globalData, const Identifier& ident, FunctionBodyNode* body, const SourceCode& source, ParameterNode* parameter = 0) JSC_FAST_CALL + : StatementNode(globalData) + , m_ident(ident) , m_parameter(parameter) , m_body(body) { - addParams(); + m_body->finishParsing(source, m_parameter.get()); } - virtual JSValue* execute(ExecState*) KJS_FAST_CALL; - virtual void streamTo(SourceStream&) const KJS_FAST_CALL; - ALWAYS_INLINE FunctionImp* makeFunction(ExecState*) KJS_FAST_CALL; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + JSFunction* makeFunction(ExecState*, ScopeChainNode*) JSC_FAST_CALL; Identifier m_ident; - private: - void addParams() KJS_FAST_CALL; + FunctionBodyNode* body() { return m_body.get(); } + private: RefPtr<ParameterNode> m_parameter; RefPtr<FunctionBodyNode> m_body; }; class CaseClauseNode : public Node { public: - CaseClauseNode(ExpressionNode* expr) KJS_FAST_CALL - : m_expr(expr) + CaseClauseNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL + : Node(globalData) + , m_expr(expr) { } - CaseClauseNode(ExpressionNode* expr, SourceElements* children) KJS_FAST_CALL - : m_expr(expr) + CaseClauseNode(JSGlobalData* globalData, ExpressionNode* expr, SourceElements* children) JSC_FAST_CALL + : Node(globalData) + , 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 void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } - JSValue* evaluate(ExecState*) KJS_FAST_CALL; - JSValue* executeStatements(ExecState*) KJS_FAST_CALL; + ExpressionNode* expr() const { return m_expr.get(); } + StatementVector& children() { return m_children; } private: RefPtr<ExpressionNode> m_expr; @@ -2803,22 +2393,23 @@ namespace KJS { class ClauseListNode : public Node { public: - ClauseListNode(CaseClauseNode* clause) KJS_FAST_CALL - : m_clause(clause) + ClauseListNode(JSGlobalData* globalData, CaseClauseNode* clause) JSC_FAST_CALL + : Node(globalData) + , m_clause(clause) { } - ClauseListNode(ClauseListNode* clauseList, CaseClauseNode* clause) KJS_FAST_CALL - : m_clause(clause) + ClauseListNode(JSGlobalData* globalData, ClauseListNode* clauseList, CaseClauseNode* clause) JSC_FAST_CALL + : Node(globalData) + , 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(); } + CaseClauseNode* getClause() const JSC_FAST_CALL { return m_clause.get(); } + ClauseListNode* getNext() const JSC_FAST_CALL { return m_next.get(); } + virtual void streamTo(SourceStream&) const JSC_FAST_CALL; + PassRefPtr<ClauseListNode> releaseNext() JSC_FAST_CALL { return m_next.release(); } virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } private: @@ -2829,14 +2420,21 @@ namespace KJS { class CaseBlockNode : public Node { public: - CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) KJS_FAST_CALL; + CaseBlockNode(JSGlobalData* globalData, ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) JSC_FAST_CALL + : Node(globalData) + , m_list1(list1) + , m_defaultClause(defaultClause) + , m_list2(list2) + { + } + + RegisterID* emitCodeForBlock(CodeGenerator&, RegisterID* input, RegisterID* dst = 0) JSC_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 void streamTo(SourceStream&) const JSC_FAST_CALL; virtual Precedence precedence() const { ASSERT_NOT_REACHED(); return PrecExpression; } private: + SwitchInfo::SwitchType tryOptimizedSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num); RefPtr<ClauseListNode> m_list1; RefPtr<CaseClauseNode> m_defaultClause; RefPtr<ClauseListNode> m_list2; @@ -2844,33 +2442,22 @@ namespace KJS { class SwitchNode : public StatementNode { public: - SwitchNode(ExpressionNode* expr, CaseBlockNode* block) KJS_FAST_CALL - : m_expr(expr) + SwitchNode(JSGlobalData* globalData, ExpressionNode* expr, CaseBlockNode* block) JSC_FAST_CALL + : StatementNode(globalData) + , 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; + virtual RegisterID* emitCode(CodeGenerator&, RegisterID* = 0) JSC_FAST_CALL; + + virtual void streamTo(SourceStream&) const JSC_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; @@ -2901,6 +2488,6 @@ namespace KJS { ClauseListNode* tail; }; -} // namespace KJS +} // namespace JSC #endif // NODES_H_ diff --git a/JavaScriptCore/kjs/nodes2string.cpp b/JavaScriptCore/kjs/nodes2string.cpp index b10e180..4afefe0 100644 --- a/JavaScriptCore/kjs/nodes2string.cpp +++ b/JavaScriptCore/kjs/nodes2string.cpp @@ -30,7 +30,7 @@ using namespace WTF; using namespace Unicode; -namespace KJS { +namespace JSC { // A simple text streaming class that helps with code indentation. @@ -78,7 +78,7 @@ static UString escapeStringForPrettyPrinting(const UString& s) UString escapedString; for (int i = 0; i < s.size(); i++) { - unsigned short c = s.data()[i].unicode(); + UChar c = s.data()[i]; switch (c) { case '\"': escapedString += "\\\""; @@ -159,7 +159,8 @@ SourceStream& SourceStream::operator<<(char c) { m_numberNeedsParens = false; m_atStartOfStatement = false; - UChar ch(c); + // use unsigned char to zero-extend instead of sign-extend + UChar ch(static_cast<unsigned char>(c)); m_string.append(ch); return *this; } @@ -296,18 +297,9 @@ 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 BooleanNode::streamTo(SourceStream& s) const { + s << (m_value ? "true" : "false"); } void NumberNode::streamTo(SourceStream& s) const @@ -317,12 +309,12 @@ void NumberNode::streamTo(SourceStream& s) const void StringNode::streamTo(SourceStream& s) const { - s << '"' << escapeStringForPrettyPrinting(m_value) << '"'; + s << '"' << escapeStringForPrettyPrinting(m_value.ustring()) << '"'; } void RegExpNode::streamTo(SourceStream& s) const { - s << '/' << m_regExp->pattern() << '/' << m_regExp->flags(); + s << '/' << m_pattern << '/' << m_flags; } void ThisNode::streamTo(SourceStream& s) const @@ -426,6 +418,11 @@ void NewExprNode::streamTo(SourceStream& s) const s << "new " << PrecMember << m_expr << m_args; } +void EvalFunctionCallNode::streamTo(SourceStream& s) const +{ + s << "eval" << m_args; +} + void FunctionCallValueNode::streamTo(SourceStream& s) const { s << PrecCall << m_expr << m_args; @@ -448,38 +445,21 @@ void FunctionCallDotNode::streamTo(SourceStream& s) const 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 +void PostfixResolveNode::streamTo(SourceStream& s) const { - bracketNodeStreamTo(s, m_base, m_subscript); - s << "++"; + s << m_ident << operatorString(m_operator); } -void PostDecBracketNode::streamTo(SourceStream& s) const +void PostfixBracketNode::streamTo(SourceStream& s) const { bracketNodeStreamTo(s, m_base, m_subscript); - s << "--"; -} - -void PostIncDotNode::streamTo(SourceStream& s) const -{ - dotNodeStreamTo(s, m_base, m_ident); - s << "++"; + s << operatorString(m_operator); } -void PostDecDotNode::streamTo(SourceStream& s) const +void PostfixDotNode::streamTo(SourceStream& s) const { dotNodeStreamTo(s, m_base, m_ident); - s << "--"; + s << operatorString(m_operator); } void PostfixErrorNode::streamTo(SourceStream& s) const @@ -528,37 +508,20 @@ void TypeOfResolveNode::streamTo(SourceStream& s) const s << "typeof " << m_ident; } -void PreIncResolveNode::streamTo(SourceStream& s) const +void PrefixResolveNode::streamTo(SourceStream& s) const { - s << "++" << m_ident; + s << operatorString(m_operator) << m_ident; } -void PreDecResolveNode::streamTo(SourceStream& s) const +void PrefixBracketNode::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 << "--"; + s << operatorString(m_operator); bracketNodeStreamTo(s, m_base, m_subscript); } -void PreIncDotNode::streamTo(SourceStream& s) const +void PrefixDotNode::streamTo(SourceStream& s) const { - s << "++"; - dotNodeStreamTo(s, m_base, m_ident); -} - -void PreDecDotNode::streamTo(SourceStream& s) const -{ - s << "--"; + s << operatorString(m_operator); dotNodeStreamTo(s, m_base, m_ident); } @@ -592,42 +555,42 @@ void LogicalNotNode::streamTo(SourceStream& s) const void MultNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "*", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), "*", m_expr1, m_expr2); } void DivNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "/", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), "/", m_expr1, m_expr2); } void ModNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "%", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), "%", m_expr1, m_expr2); } void AddNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "+", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), "+", m_expr1, m_expr2); } void SubNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "-", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), "-", m_expr1, m_expr2); } void LeftShiftNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "<<", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), "<<", m_expr1, m_expr2); } void RightShiftNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), ">>", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), ">>", m_expr1, m_expr2); } void UnsignedRightShiftNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), ">>>", m_term1, m_term2); + streamLeftAssociativeBinaryOperator(s, precedence(), ">>>", m_expr1, m_expr2); } void LessNode::streamTo(SourceStream& s) const @@ -695,14 +658,9 @@ void BitOrNode::streamTo(SourceStream& s) const streamLeftAssociativeBinaryOperator(s, precedence(), "|", m_expr1, m_expr2); } -void LogicalAndNode::streamTo(SourceStream& s) const +void LogicalOpNode::streamTo(SourceStream& s) const { - streamLeftAssociativeBinaryOperator(s, precedence(), "&&", m_expr1, m_expr2); -} - -void LogicalOrNode::streamTo(SourceStream& s) const -{ - streamLeftAssociativeBinaryOperator(s, precedence(), "||", m_expr1, m_expr2); + streamLeftAssociativeBinaryOperator(s, precedence(), (m_operator == OpLogicalAnd) ? "&&" : "||", m_expr1, m_expr2); } void ConditionalNode::streamTo(SourceStream& s) const @@ -761,11 +719,11 @@ void ConstDeclNode::streamTo(SourceStream& s) const { s << m_ident; if (m_init) - s << " = " << m_init; + s << " = " << PrecAssignment << m_init; for (ConstDeclNode* n = m_next.get(); n; n = n->m_next.get()) { - s << ", " << m_ident; - if (m_init) - s << " = " << m_init; + s << ", " << n->m_ident; + if (n->m_init) + s << " = " << PrecAssignment << n->m_init; } } @@ -814,6 +772,11 @@ void EmptyStatementNode::streamTo(SourceStream& s) const s << Endl << ';'; } +void DebuggerStatementNode::streamTo(SourceStream& s) const +{ + s << Endl << "debugger;"; +} + void ExprStatementNode::streamTo(SourceStream& s) const { s << Endl << m_expr << ';'; @@ -936,7 +899,7 @@ void SwitchNode::streamTo(SourceStream& s) const void LabelNode::streamTo(SourceStream& s) const { - s << Endl << m_label << ":" << Indent << m_statement << Unindent; + s << Endl << m_name << ":" << Indent << m_statement << Unindent; } void ThrowNode::streamTo(SourceStream& s) const @@ -970,4 +933,4 @@ void FuncExprNode::streamTo(SourceStream& s) const s << "function " << m_ident << '(' << m_parameter << ')' << m_body; } -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/number_object.cpp b/JavaScriptCore/kjs/number_object.cpp deleted file mode 100644 index 94fc7e7..0000000 --- a/JavaScriptCore/kjs/number_object.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/* - * 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 deleted file mode 100644 index 36befed..0000000 --- a/JavaScriptCore/kjs/number_object.h +++ /dev/null @@ -1,76 +0,0 @@ -// -*- 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 deleted file mode 100644 index 7c0653a..0000000 --- a/JavaScriptCore/kjs/object.cpp +++ /dev/null @@ -1,693 +0,0 @@ -// -*- 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) { -#ifdef ANDROID_FIX - if (imp == imp->_proto) - break; -#endif - 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, int attr) -{ - 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; - } - - // The put calls from JavaScript execution either have no attributes set, or in some cases - // have DontDelete set. For those calls, respect the ReadOnly flag. - bool checkReadOnly = !(attr & ~DontDelete); - - // Check if there are any setters or getters in the prototype chain - JSObject *obj = this; - bool hasGettersOrSetters = false; - while (true) { -#ifdef ANDROID_FIX - if (obj == obj->_proto) - break; -#endif - if (obj->_prop.hasGetterSetterProperties()) { - hasGettersOrSetters = true; - break; - } - - if (!obj->_proto->isObject()) - break; - - obj = static_cast<JSObject *>(obj->_proto); - } - - if (hasGettersOrSetters) { - if (checkReadOnly && !canPut(exec, propertyName)) - return; - - obj = this; - while (true) { -#ifdef ANDROID_FIX - if (obj == obj->_proto) - break; -#endif - unsigned attributes; - 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, attr, checkReadOnly); -} - -void JSObject::put(ExecState *exec, unsigned propertyName, - JSValue *value, int attr) -{ - put(exec, Identifier::from(propertyName), value, attr); -} - -// ECMA 8.6.2.3 -bool JSObject::canPut(ExecState *, const Identifier &propertyName) const -{ - unsigned attributes; - - // Don't look in the prototype here. We can always put an override - // in the object, even if the prototype has a ReadOnly property. - // Also, there is no need to check the static property table, as this - // would have been done by the subclass already. - - if (!_prop.get(propertyName, attributes)) - return true; - - return !(attributes & ReadOnly); -} - -// 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 deleted file mode 100644 index aa32738..0000000 --- a/JavaScriptCore/kjs/object.h +++ /dev/null @@ -1,607 +0,0 @@ -// -*- 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 - Internal = 1 << 4, // an internal property, set to bypass checks - Function = 1 << 5, // property is a function - only used by static hashtables - GetterSetter = 1 << 6 }; // 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* exec, const Identifier &propertyName, JSValue* value, int attr = None); - virtual void put(ExecState* exec, unsigned propertyName, JSValue* value, int attr = None); - - /** - * Used to check whether or not a particular property is allowed to be set - * on an object - * - * See ECMA 8.6.2.3 - * - * @param exec The current execution state - * @param propertyName The name of the property - * @return true if the property can be set, otherwise false - */ - /** - * Implementation of the [[CanPut]] internal property (implemented by all - * Objects) - */ - virtual bool canPut(ExecState *exec, const Identifier &propertyName) const; - - /** - * 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() { 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) { -#ifdef ANDROID_FIX - if (object == object->_proto) - return false; -#endif - 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 deleted file mode 100644 index 90eaa93..0000000 --- a/JavaScriptCore/kjs/object_object.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * 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 deleted file mode 100644 index 0ee5482..0000000 --- a/JavaScriptCore/kjs/object_object.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 index d2b3892..f2d8deb 100644 --- a/JavaScriptCore/kjs/operations.cpp +++ b/JavaScriptCore/kjs/operations.cpp @@ -1,7 +1,6 @@ -// -*- c-basic-offset: 2 -*- /* - * This file is part of the KDE libraries - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,8 +22,9 @@ #include "config.h" #include "operations.h" -#include "internal.h" -#include "object.h" +#include "Error.h" +#include "JSObject.h" +#include "JSString.h" #include <math.h> #include <stdio.h> #include <wtf/MathExtras.h> @@ -33,96 +33,43 @@ #include <float.h> #endif -namespace KJS { +namespace JSC { // ECMA 11.9.3 -bool equal(ExecState *exec, JSValue *v1, JSValue *v2) +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; + if (JSImmediate::areBothImmediateNumbers(v1, v2)) + return v1 == v2; + + return equalSlowCaseInline(exec, v1, v2); } -bool strictEqual(ExecState *exec, JSValue *v1, JSValue *v2) +bool equalSlowCase(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 equalSlowCaseInline(exec, v1, v2); +} + +bool strictEqual(JSValue* v1, JSValue* v2) +{ + if (JSImmediate::areBothImmediate(v1, v2)) + return v1 == v2; + + if (JSImmediate::isEitherImmediate(v1, v2) & (v1 != JSImmediate::from(0)) & (v2 != JSImmediate::from(0))) 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; + + return strictEqualSlowCaseInline(v1, v2); +} + +bool strictEqualSlowCase(JSValue* v1, JSValue* v2) +{ + return strictEqualSlowCaseInline(v1, v2); } +NEVER_INLINE JSValue* throwOutOfMemoryError(ExecState* exec) +{ + JSObject* error = Error::create(exec, GeneralError, "Out of memory"); + exec->setException(error); + return error; } + +} // namespace JSC diff --git a/JavaScriptCore/kjs/operations.h b/JavaScriptCore/kjs/operations.h index 96656f0..fad9720 100644 --- a/JavaScriptCore/kjs/operations.h +++ b/JavaScriptCore/kjs/operations.h @@ -1,4 +1,3 @@ -// -*- c-basic-offset: 2 -*- /* * This file is part of the KDE libraries * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) @@ -23,13 +22,116 @@ #ifndef _KJS_OPERATIONS_H_ #define _KJS_OPERATIONS_H_ -namespace KJS { +#include "JSImmediate.h" +#include "JSNumberCell.h" +#include "JSString.h" - class ExecState; - class JSValue; +namespace JSC { - bool equal(ExecState *exec, JSValue *v1, JSValue *v2); - bool strictEqual(ExecState *exec, JSValue *v1, JSValue *v2); + // ECMA 11.9.3 + bool equal(ExecState*, JSValue*, JSValue*); + bool equalSlowCase(ExecState*, JSValue*, JSValue*); + + ALWAYS_INLINE bool equalSlowCaseInline(ExecState* exec, JSValue* v1, JSValue* v2) + { + ASSERT(!JSImmediate::areBothImmediateNumbers(v1, v2)); + + do { + if (v1->isNumber() && v2->isNumber()) + return v1->uncheckedGetNumber() == v2->uncheckedGetNumber(); + + bool s1 = v1->isString(); + bool s2 = v2->isString(); + if (s1 && s2) + return asString(v1)->value() == asString(v2)->value(); + + if (v1->isUndefinedOrNull()) { + if (v2->isUndefinedOrNull()) + return true; + if (JSImmediate::isImmediate(v2)) + return false; + return v2->asCell()->structureID()->typeInfo().masqueradesAsUndefined(); + } + + if (v2->isUndefinedOrNull()) { + if (JSImmediate::isImmediate(v1)) + return false; + return v1->asCell()->structureID()->typeInfo().masqueradesAsUndefined(); + } + + if (v1->isObject()) { + if (v2->isObject()) + return v1 == v2; + JSValue* p1 = v1->toPrimitive(exec); + if (exec->hadException()) + return false; + v1 = p1; + if (JSImmediate::areBothImmediateNumbers(v1, v2)) + return v1 == v2; + continue; + } + + if (v2->isObject()) { + JSValue* p2 = v2->toPrimitive(exec); + if (exec->hadException()) + return false; + v2 = p2; + if (JSImmediate::areBothImmediateNumbers(v1, v2)) + return v1 == v2; + continue; + } + + if (s1 || s2) { + double d1 = v1->toNumber(exec); + double d2 = v2->toNumber(exec); + return d1 == d2; + } + + if (v1->isBoolean()) { + if (v2->isNumber()) + return static_cast<double>(v1->getBoolean()) == v2->uncheckedGetNumber(); + } else if (v2->isBoolean()) { + if (v1->isNumber()) + return v1->uncheckedGetNumber() == static_cast<double>(v2->getBoolean()); + } + + return v1 == v2; + } while (true); + } + + + bool strictEqual(JSValue*, JSValue*); + bool strictEqualSlowCase(JSValue*, JSValue*); + + inline bool strictEqualSlowCaseInline(JSValue* v1, JSValue* v2) + { + ASSERT(!JSImmediate::areBothImmediate(v1, v2)); + + if (JSImmediate::isEitherImmediate(v1, v2)) { + ASSERT(v1 == JSImmediate::zeroImmediate() || v2 == JSImmediate::zeroImmediate()); + ASSERT(v1 != v2); + + // The reason we can't just return false here is that 0 === -0, + // and while the former is an immediate number, the latter is not. + if (v1 == JSImmediate::zeroImmediate()) + return asCell(v2)->isNumber() && asNumberCell(v2)->value() == 0; + return asCell(v1)->isNumber() && asNumberCell(v1)->value() == 0; + } + + if (asCell(v1)->isNumber()) { + return asCell(v2)->isNumber() + && asNumberCell(v1)->value() == asNumberCell(v2)->value(); + } + + if (asCell(v1)->isString()) { + return asCell(v2)->isString() + && asString(v1)->value() == asString(v2)->value(); + } + + return v1 == v2; + } + + JSValue* throwOutOfMemoryError(ExecState*); } #endif diff --git a/JavaScriptCore/kjs/property_map.cpp b/JavaScriptCore/kjs/property_map.cpp deleted file mode 100644 index 1da4917..0000000 --- a/JavaScriptCore/kjs/property_map.cpp +++ /dev/null @@ -1,850 +0,0 @@ -/* - * 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 deleted file mode 100644 index 269e911..0000000 --- a/JavaScriptCore/kjs/property_map.h +++ /dev/null @@ -1,184 +0,0 @@ -// -*- 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 deleted file mode 100644 index ef2525f..0000000 --- a/JavaScriptCore/kjs/property_slot.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// -*- 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 deleted file mode 100644 index 4ec8b46..0000000 --- a/JavaScriptCore/kjs/property_slot.h +++ /dev/null @@ -1,142 +0,0 @@ -// -*- 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 index 912e45c..b317424 100644 --- a/JavaScriptCore/kjs/protect.h +++ b/JavaScriptCore/kjs/protect.h @@ -1,7 +1,5 @@ -// -*- c-basic-offset: 2 -*- /* - * This file is part of the KDE libraries - * Copyright (C) 2004 Apple Computer, Inc. + * Copyright (C) 2004, 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 @@ -21,123 +19,136 @@ */ -#ifndef _KJS_PROTECT_H_ -#define _KJS_PROTECT_H_ +#ifndef protect_h +#define protect_h -#include "value.h" +#include "JSCell.h" #include "collector.h" -#include "JSLock.h" -namespace KJS { +namespace JSC { - inline void gcProtect(JSValue *val) - { - Collector::protect(val); + inline void gcProtect(JSCell* val) + { + Heap::heap(val)->protect(val); } - inline void gcUnprotect(JSValue *val) - { - Collector::unprotect(val); + inline void gcUnprotect(JSCell* val) + { + Heap::heap(val)->unprotect(val); } - inline void gcProtectNullTolerant(JSValue *val) + inline void gcProtectNullTolerant(JSCell* val) { if (val) gcProtect(val); } - inline void gcUnprotectNullTolerant(JSValue *val) + inline void gcUnprotectNullTolerant(JSCell* val) { if (val) gcUnprotect(val); } + inline void gcProtect(JSValue* value) + { + if (JSImmediate::isImmediate(value)) + return; + gcProtect(asCell(value)); + } + + inline void gcUnprotect(JSValue* value) + { + if (JSImmediate::isImmediate(value)) + return; + gcUnprotect(asCell(value)); + } + + inline void gcProtectNullTolerant(JSValue* value) + { + if (!value || JSImmediate::isImmediate(value)) + return; + gcProtect(asCell(value)); + } + + inline void gcUnprotectNullTolerant(JSValue* value) + { + if (!value || JSImmediate::isImmediate(value)) + return; + gcUnprotect(asCell(value)); + } + // 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() : m_ptr(0) { } + ProtectedPtr(T* ptr); + ProtectedPtr(const ProtectedPtr&); ~ProtectedPtr(); - template <class U> ProtectedPtr(const ProtectedPtr<U> &); + 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; } + 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; } + bool operator!() const { return !m_ptr; } - ProtectedPtr &operator=(const ProtectedPtr &); - ProtectedPtr &operator=(T *); + ProtectedPtr& operator=(const ProtectedPtr&); + ProtectedPtr& operator=(T*); private: - T *m_ptr; + T* m_ptr; }; - template <class T> ProtectedPtr<T>::ProtectedPtr(T *ptr) + template <class T> ProtectedPtr<T>::ProtectedPtr(T* ptr) : m_ptr(ptr) { - if (ptr) { - JSLock lock; - gcProtect(ptr); - } + gcProtectNullTolerant(m_ptr); } - template <class T> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr &o) + template <class T> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr& o) : m_ptr(o.get()) { - if (T *ptr = m_ptr) { - JSLock lock; - gcProtect(ptr); - } + gcProtectNullTolerant(m_ptr); } template <class T> ProtectedPtr<T>::~ProtectedPtr() { - if (T *ptr = m_ptr) { - JSLock lock; - gcUnprotect(ptr); - } + gcUnprotectNullTolerant(m_ptr); } - template <class T> template <class U> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr<U> &o) + 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); - } + gcProtectNullTolerant(m_ptr); } - template <class T> ProtectedPtr<T> &ProtectedPtr<T>::operator=(const ProtectedPtr<T> &o) + template <class T> ProtectedPtr<T>& ProtectedPtr<T>::operator=(const ProtectedPtr<T>& o) { - JSLock lock; - T *optr = o.m_ptr; + T* optr = o.m_ptr; gcProtectNullTolerant(optr); gcUnprotectNullTolerant(m_ptr); m_ptr = optr; - return *this; - } + return *this; + } - template <class T> inline ProtectedPtr<T> &ProtectedPtr<T>::operator=(T *optr) + 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(); } - 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 +} // namespace JSC -#endif +#endif // protect_h diff --git a/JavaScriptCore/kjs/regexp.cpp b/JavaScriptCore/kjs/regexp.cpp index 4839c86..7397232 100644 --- a/JavaScriptCore/kjs/regexp.cpp +++ b/JavaScriptCore/kjs/regexp.cpp @@ -1,8 +1,6 @@ -// -*- c-basic-offset: 2 -*- /* - * This file is part of the KDE libraries * Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org) - * Copyright (c) 2007, Apple Inc. + * 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 @@ -23,33 +21,52 @@ #include "config.h" #include "regexp.h" +#include "CTI.h" #include "lexer.h" +#include <pcre/pcre.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <wrec/WREC.h> #include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> -namespace KJS { +namespace JSC { -RegExp::RegExp(const UString& pattern) - : m_pattern(pattern) - , m_flagBits(0) - , m_constructionError(0) - , m_numSubpatterns(0) +inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern) + : m_pattern(pattern) + , m_flagBits(0) + , m_regExp(0) + , m_constructionError(0) + , m_numSubpatterns(0) { - m_regExp = jsRegExpCompile(reinterpret_cast<const ::UChar*>(pattern.data()), pattern.size(), +#if ENABLE(WREC) + m_wrecFunction = CTI::compileRegExp(globalData->machine, pattern, &m_numSubpatterns, &m_constructionError); + if (m_wrecFunction) + return; + // Fall through to non-WREC case. +#else + UNUSED_PARAM(globalData); +#endif + m_regExp = jsRegExpCompile(reinterpret_cast<const UChar*>(pattern.data()), pattern.size(), JSRegExpDoNotIgnoreCase, JSRegExpSingleLine, &m_numSubpatterns, &m_constructionError); } -RegExp::RegExp(const UString& pattern, const UString& flags) - : m_pattern(pattern) - , m_flags(flags) - , m_flagBits(0) - , m_constructionError(0) - , m_numSubpatterns(0) +PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& pattern) +{ + return adoptRef(new RegExp(globalData, pattern)); +} + +inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern, const UString& flags) + : m_pattern(pattern) + , m_flags(flags) + , m_flagBits(0) + , m_regExp(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. + // String::match and RegExpObject::match. if (flags.find('g') != -1) m_flagBits |= Global; @@ -65,56 +82,101 @@ RegExp::RegExp(const UString& pattern, const UString& flags) m_flagBits |= Multiline; multilineOption = JSRegExpMultiline; } - - m_regExp = jsRegExpCompile(reinterpret_cast<const ::UChar*>(pattern.data()), pattern.size(), + +#if ENABLE(WREC) + m_wrecFunction = CTI::compileRegExp(globalData->machine, pattern, &m_numSubpatterns, &m_constructionError, (m_flagBits & IgnoreCase), (m_flagBits & Multiline)); + if (m_wrecFunction) + return; + // Fall through to non-WREC case. +#else + UNUSED_PARAM(globalData); +#endif + m_regExp = jsRegExpCompile(reinterpret_cast<const UChar*>(pattern.data()), pattern.size(), ignoreCaseOption, multilineOption, &m_numSubpatterns, &m_constructionError); } +PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& pattern, const UString& flags) +{ + return adoptRef(new RegExp(globalData, pattern, flags)); +} + RegExp::~RegExp() { jsRegExpFree(m_regExp); +#if ENABLE(WREC) + if (m_wrecFunction) + WTF::fastFreeExecutable(m_wrecFunction); +#endif } int RegExp::match(const UString& s, int i, OwnArrayPtr<int>* ovector) { - if (i < 0) - i = 0; - if (ovector) - ovector->clear(); + if (i < 0) + i = 0; + if (ovector) + ovector->clear(); - if (i > s.size() || s.isNull()) - return -1; + if (i > s.size() || s.isNull()) + return -1; - if (!m_regExp) - return -1; +#if ENABLE(WREC) + if (m_wrecFunction) { + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + int* offsetVector = new int [offsetVectorSize]; + for (int j = 0; j < offsetVectorSize; ++j) + offsetVector[j] = -1; + + OwnArrayPtr<int> nonReturnedOvector; + if (!ovector) + nonReturnedOvector.set(offsetVector); + else + ovector->set(offsetVector); + + int result = reinterpret_cast<WRECFunction>(m_wrecFunction)(s.data(), i, s.size(), offsetVector); - // 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) { + if (result < 0) { #ifndef NDEBUG - if (numMatches != JSRegExpErrorNoMatch) - fprintf(stderr, "jsRegExpExecute failed with result %d\n", numMatches); + // TODO: define up a symbol, rather than magic -1 + if (result != -1) + fprintf(stderr, "jsRegExpExecute failed with result %d\n", result); #endif - if (ovector) - ovector->clear(); - return -1; - } + if (ovector) + ovector->clear(); + } + return result; + } else +#endif + if (m_regExp) { + // 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]; + return offsetVector[0]; + } + + return -1; } -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/regexp.h b/JavaScriptCore/kjs/regexp.h index 2c62085..1842d94 100644 --- a/JavaScriptCore/kjs/regexp.h +++ b/JavaScriptCore/kjs/regexp.h @@ -1,7 +1,6 @@ -// -*- c-basic-offset: 2 -*- /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2007 Apple Inc. All rights reserved. + * 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 @@ -23,53 +22,55 @@ #define KJS_REGEXP_H #include "ustring.h" -#include <pcre/pcre.h> -#include <sys/types.h> -#include <wtf/OwnArrayPtr.h> +#include <wtf/Forward.h> #include <wtf/RefCounted.h> -namespace KJS { +struct JSRegExp; - class RegExp : public RefCounted<RegExp> { - private: - enum { - Global = 1, - IgnoreCase = 2, - Multiline = 4 - }; +namespace JSC { + + class JSGlobalData; + + class RegExp : public RefCounted<RegExp> { + public: + static PassRefPtr<RegExp> create(JSGlobalData*, const UString& pattern); + static PassRefPtr<RegExp> create(JSGlobalData*, 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; } - public: - RegExp(const UString& pattern); - RegExp(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; } - 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; } - 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; } - int match(const UString&, int offset, OwnArrayPtr<int>* ovector = 0); - unsigned numSubpatterns() const { return m_numSubpatterns; } + private: + RegExp(JSGlobalData*, const UString& pattern); + RegExp(JSGlobalData*, const UString& pattern, const UString& flags); - private: - void compile(); - - // Data supplied by caller. - 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; + void compile(); - // Data supplied by PCRE. - JSRegExp* m_regExp; - const char* m_constructionError; - unsigned m_numSubpatterns; - }; + enum FlagBits { Global = 1, IgnoreCase = 2, Multiline = 4 }; -} // namespace + 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; +#if ENABLE(WREC) + // Called as a WRECFunction + void* m_wrecFunction; #endif + }; + +} // namespace JSC + +#endif // KJS_REGEXP_H diff --git a/JavaScriptCore/kjs/regexp_object.cpp b/JavaScriptCore/kjs/regexp_object.cpp deleted file mode 100644 index 217c8db..0000000 --- a/JavaScriptCore/kjs/regexp_object.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/* - * 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 = new RegExp(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, int attributes) -{ - lookupPut<RegExpImp, JSObject>(exec, propertyName, value, attributes, &RegExpImpTable, this); -} - -void RegExpImp::putValueProperty(ExecState* exec, int token, JSValue* value, int) -{ - 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, int attr) -{ - lookupPut<RegExpObjectImp, InternalFunctionImp>(exec, propertyName, value, attr, &RegExpObjectImpTable, this); -} - -void RegExpObjectImp::putValueProperty(ExecState *exec, int token, JSValue *value, int) -{ - 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, new RegExp(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 deleted file mode 100644 index 1ce2f16..0000000 --- a/JavaScriptCore/kjs/regexp_object.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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*, int attributes = None); - void putValueProperty(ExecState*, int token, JSValue*, int attributes); - - 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*, int attributes = None); - void putValueProperty(ExecState*, int token, JSValue*, int attributes); - 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 deleted file mode 100644 index aba066a..0000000 --- a/JavaScriptCore/kjs/scope_chain.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 deleted file mode 100644 index 7441cb8..0000000 --- a/JavaScriptCore/kjs/scope_chain.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 deleted file mode 100644 index bc4d5f8..0000000 --- a/JavaScriptCore/kjs/scope_chain_mark.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 deleted file mode 100644 index 53b6ada..0000000 --- a/JavaScriptCore/kjs/string_object.cpp +++ /dev/null @@ -1,1055 +0,0 @@ -// -*- 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, int attr) -{ - if (propertyName == exec->propertyNames().length) - return; - JSObject::put(exec, propertyName, value, attr); -} - -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; - RegExp* reg; - RegExp* tmpReg = 0; - 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 = tmpReg = new RegExp(a0->toString(exec)); - } - RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); - int pos; - int matchLength; - regExpObj->performMatch(reg, 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, 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); - } - } - delete tmpReg; - 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; - RegExp* reg; - RegExp* tmpReg = 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 = tmpReg = new RegExp(a0->toString(exec)); - } - RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor()); - int pos; - int matchLength; - regExpObj->performMatch(reg, u, 0, pos, matchLength); - delete tmpReg; - 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 deleted file mode 100644 index 3bc9cb2..0000000 --- a/JavaScriptCore/kjs/string_object.h +++ /dev/null @@ -1,153 +0,0 @@ -// -*- 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*, int attr = None); - 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 deleted file mode 100644 index 25c807e..0000000 --- a/JavaScriptCore/kjs/testkjs.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// -*- c-basic-offset: 2 -*- -/* - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2004-2007 Apple Inc. - * 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 "collector.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); - -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 GlobalImp : public JSGlobalObject { -public: - virtual UString className() const { return "global"; } -}; -COMPILE_ASSERT(!IsInteger<GlobalImp>::value, WTF_IsInteger_GlobalImp_false); - -class TestFunctionImp : public JSObject { -public: - enum TestFunctionType { Print, Debug, Quit, GC, Version, Run, Load }; - - TestFunctionImp(TestFunctionType i, int length); - virtual bool implementsCall() const { return true; } - virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List &args); - -private: - TestFunctionType m_type; -}; - -TestFunctionImp::TestFunctionImp(TestFunctionType i, int length) - : JSObject() - , m_type(i) -{ - putDirect(Identifier("length"), length, DontDelete | ReadOnly | DontEnum); -} - -JSValue* TestFunctionImp::callAsFunction(ExecState* exec, JSObject*, const List &args) -{ - switch (m_type) { - case Print: - printf("%s\n", args[0]->toString(exec).UTF8String().c_str()); - return jsUndefined(); - case Debug: - fprintf(stderr, "--> %s\n", args[0]->toString(exec).UTF8String().c_str()); - return jsUndefined(); - case GC: - { - JSLock lock; - Collector::collect(); - return jsUndefined(); - } - case Version: - // 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(); - case Run: - { - 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()); - } - case Load: - { - 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(); - } - case Quit: - exit(0); - default: - abort(); - } - return 0; -} - -// 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 GlobalImp* createGlobalObject() -{ - GlobalImp* global = new GlobalImp; - - // add debug() function - global->put(global->globalExec(), "debug", new TestFunctionImp(TestFunctionImp::Debug, 1)); - // add "print" for compatibility with the mozilla js shell - global->put(global->globalExec(), "print", new TestFunctionImp(TestFunctionImp::Print, 1)); - // add "quit" for compatibility with the mozilla js shell - global->put(global->globalExec(), "quit", new TestFunctionImp(TestFunctionImp::Quit, 0)); - // add "gc" for compatibility with the mozilla js shell - global->put(global->globalExec(), "gc", new TestFunctionImp(TestFunctionImp::GC, 0)); - // add "version" for compatibility with the mozilla js shell - global->put(global->globalExec(), "version", new TestFunctionImp(TestFunctionImp::Version, 1)); - global->put(global->globalExec(), "run", new TestFunctionImp(TestFunctionImp::Run, 1)); - global->put(global->globalExec(), "load", new TestFunctionImp(TestFunctionImp::Load, 1)); - - Interpreter::setShouldPrintExceptions(true); - return global; -} - -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, bool prettyPrint) -{ - GlobalImp* globalObject = createGlobalObject(); - 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 parseArguments(int argc, char** argv, Vector<UString>& fileNames, bool& prettyPrint) -{ - if (argc < 2) { - fprintf(stderr, "Usage: testkjs file1 [file2...]\n"); - exit(-1); - } - - for (int i = 1; i < argc; i++) { - const char* fileName = argv[i]; - if (strcmp(fileName, "-f") == 0) // mozilla test driver script uses "-f" prefix for files - continue; - if (strcmp(fileName, "-p") == 0) { - prettyPrint = true; - continue; - } - fileNames.append(fileName); - } -} - -int kjsmain(int argc, char** argv) -{ - JSLock lock; - - bool prettyPrint = false; - Vector<UString> fileNames; - parseArguments(argc, argv, fileNames, prettyPrint); - - bool success = runWithScripts(fileNames, 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 deleted file mode 100644 index ab3016b..0000000 --- a/JavaScriptCore/kjs/testkjs.pro +++ /dev/null @@ -1,34 +0,0 @@ -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 -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 deleted file mode 100644 index 603b2a2..0000000 --- a/JavaScriptCore/kjs/types.h +++ /dev/null @@ -1,25 +0,0 @@ -// -*- 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 index b658a8d..3a85b1d 100644 --- a/JavaScriptCore/kjs/ustring.cpp +++ b/JavaScriptCore/kjs/ustring.cpp @@ -1,7 +1,6 @@ -// -*- 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) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * * This library is free software; you can redistribute it and/or @@ -24,10 +23,9 @@ #include "config.h" #include "ustring.h" -#include "JSLock.h" +#include "JSGlobalObjectFunctions.h" #include "collector.h" #include "dtoa.h" -#include "function.h" #include "identifier.h" #include "operations.h" #include <ctype.h> @@ -36,8 +34,8 @@ #include <math.h> #include <stdio.h> #include <stdlib.h> -#include <wtf/Assertions.h> #include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> #include <wtf/MathExtras.h> #include <wtf/Vector.h> #include <wtf/unicode/UTF8.h> @@ -53,20 +51,25 @@ using namespace WTF; using namespace WTF::Unicode; using namespace std; -namespace KJS { +// This can be tuned differently per platform by putting platform #ifs right here. +// If you don't define this macro at all, then copyChars will just call directly +// to memcpy. +#define USTRING_COPY_CHARS_INLINE_CUTOFF 20 +namespace JSC { + 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 size_t overflowIndicator() { return std::numeric_limits<size_t>::max(); } +static inline size_t maxUChars() { return std::numeric_limits<size_t>::max() / sizeof(UChar); } static inline UChar* allocChars(size_t length) { ASSERT(length); if (length > maxUChars()) return 0; - return static_cast<UChar*>(fastMalloc(sizeof(UChar) * length)); + return static_cast<UChar*>(tryFastMalloc(sizeof(UChar) * length)); } static inline UChar* reallocChars(UChar* buffer, size_t length) @@ -74,177 +77,211 @@ 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)); + return static_cast<UChar*>(tryFastRealloc(buffer, sizeof(UChar) * length)); +} + +static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) +{ +#ifdef USTRING_COPY_CHARS_INLINE_CUTOFF + if (numCharacters <= USTRING_COPY_CHARS_INLINE_CUTOFF) { + for (unsigned i = 0; i < numCharacters; ++i) + destination[i] = source[i]; + return; + } +#endif + memcpy(destination, source, numCharacters * sizeof(UChar)); } COMPILE_ASSERT(sizeof(UChar) == 2, uchar_is_2_bytes) -CString::CString(const char *c) +CString::CString(const char* c) + : m_length(strlen(c)) + , m_data(new char[m_length + 1]) { - length = strlen(c); - data = new char[length+1]; - memcpy(data, c, length + 1); + memcpy(m_data, c, m_length + 1); } -CString::CString(const char *c, size_t len) +CString::CString(const char* c, size_t length) + : m_length(length) + , m_data(new char[length + 1]) { - length = len; - data = new char[len+1]; - memcpy(data, c, len); - data[len] = 0; + memcpy(m_data, c, m_length); + m_data[m_length] = 0; } -CString::CString(const CString &b) +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; + m_length = b.m_length; + if (b.m_data) { + m_data = new char[m_length + 1]; + memcpy(m_data, b.m_data, m_length + 1); + } else + m_data = 0; } CString::~CString() { - delete [] data; + delete [] m_data; } -CString &CString::append(const CString &t) +CString CString::adopt(char* c, size_t length) { - 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; + CString s; + s.m_data = c; + s.m_length = length; + return s; +} - delete [] data; - data = n; +CString& CString::append(const CString& t) +{ + char* n; + n = new char[m_length + t.m_length + 1]; + if (m_length) + memcpy(n, m_data, m_length); + if (t.m_length) + memcpy(n + m_length, t.m_data, t.m_length); + m_length += t.m_length; + n[m_length] = 0; + + delete [] m_data; + m_data = n; - return *this; + return *this; } -CString &CString::operator=(const char *c) +CString& CString::operator=(const char* c) { - if (data) - delete [] data; - length = strlen(c); - data = new char[length+1]; - memcpy(data, c, length + 1); + if (m_data) + delete [] m_data; + m_length = strlen(c); + m_data = new char[m_length + 1]; + memcpy(m_data, c, m_length + 1); - return *this; + return *this; } -CString &CString::operator=(const CString &str) +CString& CString::operator=(const CString& str) { - if (this == &str) - return *this; + 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; + if (m_data) + delete [] m_data; + m_length = str.m_length; + if (str.m_data) { + m_data = new char[m_length + 1]; + memcpy(m_data, str.m_data, m_length + 1); + } else + m_data = 0; - return *this; + 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); + 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); +// These static strings are immutable, except for rc, whose initial value is chosen to +// reduce the possibility of it becoming zero due to ref/deref not being thread-safe. +static UChar sharedEmptyChar; +UString::Rep UString::Rep::null = { 0, 0, INT_MAX / 2, 0, 1, &UString::Rep::null, 0, 0, 0, 0, 0, 0 }; +UString::Rep UString::Rep::empty = { 0, 0, INT_MAX / 2, 0, 1, &UString::Rep::empty, 0, &sharedEmptyChar, 0, 0, 0, 0 }; - int sizeInBytes = l * sizeof(UChar); - UChar *copyD = static_cast<UChar *>(fastMalloc(sizeInBytes)); - memcpy(copyD, d, sizeInBytes); +static char* statBuffer = 0; // Only used for debugging via UString::ascii(). - return create(copyD, l); +PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar* d, int l) +{ + UChar* copyD = static_cast<UChar*>(fastMalloc(l * sizeof(UChar))); + copyChars(copyD, d, l); + return create(copyD, l); } -PassRefPtr<UString::Rep> UString::Rep::create(UChar *d, int 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); + Rep* r = new Rep; + r->offset = 0; + r->len = l; + r->rc = 1; + r->_hash = 0; + r->m_identifierTable = 0; + r->baseString = r; + r->reportedCost = 0; + r->buf = d; + r->usedCapacity = l; + r->capacity = l; + r->usedPreCapacity = 0; + r->preCapacity = 0; + + r->checkConsistency(); + + // 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(base); + base->checkConsistency(); + + 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->m_identifierTable = 0; + r->baseString = base.releaseRef(); + r->reportedCost = 0; + r->buf = 0; + r->usedCapacity = 0; + r->capacity = 0; + r->usedPreCapacity = 0; + r->preCapacity = 0; + + r->checkConsistency(); + + // steal the single reference this Rep was created with + return adoptRef(r); +} - ASSERT(-(offset + baseOffset) <= base->usedPreCapacity); - ASSERT(offset + baseOffset + length <= base->usedCapacity); +PassRefPtr<UString::Rep> UString::Rep::createFromUTF8(const char* string) +{ + if (!string) + return &UString::Rep::null; - 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; + size_t length = strlen(string); + Vector<UChar, 1024> buffer(length); + UChar* p = buffer.data(); + if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length)) + return &UString::Rep::null; - // steal the single reference this Rep was created with - return adoptRef(r); + return UString::Rep::createCopying(buffer.data(), p - buffer.data()); } void UString::Rep::destroy() { - ASSERT(JSLock::lockCount() > 0); - - if (isIdentifier) - Identifier::remove(this); - if (baseString != this) { - baseString->deref(); - } else { - fastFree(buf); - } - delete this; + checkConsistency(); + + // Static null and empty strings can never be destroyed, but we cannot rely on + // reference counting, because ref/deref are not thread-safe. + if (!isStatic()) { + if (identifierTable()) + Identifier::remove(this); + if (baseString == this) + fastFree(buf); + else + baseString->deref(); + + delete this; + } } // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's @@ -253,96 +290,126 @@ 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; +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]; + tmp = (s[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } + + // Handle end case + if (rem) { + hash += 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; } // Paul Hsieh's SuperFastHash // http://www.azillionmonkeys.com/qed/hash.html -unsigned UString::Rep::computeHash(const char *s) +unsigned UString::Rep::computeHash(const char* s, int l) { - // 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 + // 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 rem = l & 1; + l >>= 1; + + // Main loop + for (; l > 0; l--) { + hash += static_cast<unsigned char>(s[0]); + tmp = (static_cast<unsigned char>(s[1]) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } - 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; + // Handle end case + if (rem) { + hash += static_cast<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; - return hash; + // 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; } +#ifndef NDEBUG +void UString::Rep::checkConsistency() const +{ + // Only base strings have non-zero shared data. + if (this != baseString) { + ASSERT(!buf); + ASSERT(!usedCapacity); + ASSERT(!capacity); + ASSERT(!usedPreCapacity); + ASSERT(!preCapacity); + } + + // There is no recursion for base strings. + ASSERT(baseString == baseString->baseString); + + if (isStatic()) { + // There are only two static strings: null and empty. + ASSERT(!len); + + // Static strings cannot get in identifier tables, because they are globally shared. + ASSERT(!identifierTable()); + } + + // The string fits in buffer. + ASSERT(baseString->usedPreCapacity <= baseString->preCapacity); + ASSERT(baseString->usedCapacity <= baseString->capacity); + ASSERT(-offset <= baseString->usedPreCapacity); + ASSERT(offset + len <= baseString->usedCapacity); +} +#endif + // put these early so they can be inlined -inline size_t UString::expandedSize(size_t size, size_t otherSize) const +static inline size_t expandedSize(size_t size, size_t otherSize) { // Do the size calculation in two parts, returning overflowIndicator if // we overflow the maximum value that we can handle. @@ -359,97 +426,112 @@ inline size_t UString::expandedSize(size_t size, size_t otherSize) const inline int UString::usedCapacity() const { - return m_rep->baseString->usedCapacity; + return m_rep->baseString->usedCapacity; } inline int UString::usedPreCapacity() const { - return m_rep->baseString->usedPreCapacity; + 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; +static inline bool expandCapacity(UString::Rep* rep, int requiredLength) +{ + rep->checkConsistency(); + + UString::Rep* r = 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; + return false; + } + r->capacity = newCapacity - r->preCapacity; } - r->capacity = newCapacity - r->preCapacity; - } - if (requiredLength > r->usedCapacity) { - r->usedCapacity = requiredLength; - } + if (requiredLength > r->usedCapacity) + r->usedCapacity = requiredLength; + + rep->checkConsistency(); + return true; +} + +void UString::expandCapacity(int requiredLength) +{ + if (!JSC::expandCapacity(m_rep.get(), requiredLength)) + makeNull(); } void UString::expandPreCapacity(int requiredPreCap) { - Rep* r = m_rep->baseString; + m_rep->checkConsistency(); - if (requiredPreCap > r->preCapacity) { - size_t newCapacity = expandedSize(requiredPreCap, r->capacity); - int delta = newCapacity - r->capacity - r->preCapacity; + Rep* r = m_rep->baseString; - UChar* newBuf = allocChars(newCapacity); - if (!newBuf) { - m_rep = &Rep::null; - return; + if (requiredPreCap > r->preCapacity) { + size_t newCapacity = expandedSize(requiredPreCap, r->capacity); + int delta = newCapacity - r->capacity - r->preCapacity; + + UChar* newBuf = allocChars(newCapacity); + if (!newBuf) { + makeNull(); + return; + } + copyChars(newBuf + delta, r->buf, r->capacity + r->preCapacity); + fastFree(r->buf); + r->buf = newBuf; + + r->preCapacity = newCapacity - r->capacity; } - memcpy(newBuf + delta, r->buf, (r->capacity + r->preCapacity) * sizeof(UChar)); - fastFree(r->buf); - r->buf = newBuf; + if (requiredPreCap > r->usedPreCapacity) + r->usedPreCapacity = requiredPreCap; - r->preCapacity = newCapacity - r->capacity; - } - if (requiredPreCap > r->usedPreCapacity) { - r->usedPreCapacity = requiredPreCap; - } + m_rep->checkConsistency(); } -UString::UString(const char *c) +PassRefPtr<UString::Rep> createRep(const char* c) { - if (!c) { - m_rep = &Rep::null; - return; - } + if (!c) + return &UString::Rep::null; - if (!c[0]) { - m_rep = &Rep::empty; - return; - } + if (!c[0]) + return &UString::Rep::empty; + + size_t length = strlen(c); + UChar* d = allocChars(length); + if (!d) + return &UString::Rep::null; + else { + for (size_t i = 0; i < length; i++) + d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend + return UString::Rep::create(d, static_cast<int>(length)); + } - 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) +UString::UString(const char* c) + : m_rep(createRep(c)) { - if (length == 0) - m_rep = &Rep::empty; - else - m_rep = Rep::createCopying(c, length); } -UString::UString(UChar *c, int length, bool copy) +UString::UString(const UChar* c, int length) { - if (length == 0) - m_rep = &Rep::empty; - else if (copy) - m_rep = Rep::createCopying(c, length); - else - m_rep = Rep::create(c, 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) @@ -460,435 +542,647 @@ UString::UString(const Vector<UChar>& buffer) m_rep = Rep::createCopying(buffer.data(), buffer.size()); } +static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const UChar* tData, int tSize) +{ + RefPtr<UString::Rep> rep = r; + + rep->checkConsistency(); + + int thisSize = rep->size(); + int thisOffset = rep->offset; + int length = thisSize + tSize; + + // possible cases: + if (tSize == 0) { + // t is empty + } else if (thisSize == 0) { + // this is empty + rep = UString::Rep::createCopying(tData, tSize); + } else if (rep->baseIsSelf() && rep->rc == 1) { + // this is direct and has refcount of 1 (so we can just alter it directly) + if (!expandCapacity(rep.get(), thisOffset + length)) + rep = &UString::Rep::null; + if (rep->data()) { + copyChars(rep->data() + thisSize, tData, tSize); + rep->len = length; + rep->_hash = 0; + } + } else if (thisOffset + thisSize == rep->baseString->usedCapacity && thisSize >= minShareSize) { + // this reaches the end of the buffer - extend it if it's long enough to append to + if (!expandCapacity(rep.get(), thisOffset + length)) + rep = &UString::Rep::null; + if (rep->data()) { + copyChars(rep->data() + thisSize, tData, tSize); + rep = UString::Rep::create(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) + rep = &UString::Rep::null; + else { + copyChars(d, rep->data(), thisSize); + copyChars(d + thisSize, tData, tSize); + rep = UString::Rep::create(d, length); + rep->capacity = newCapacity; + } + } + + rep->checkConsistency(); + + return rep.release(); +} + +static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const char* t) +{ + RefPtr<UString::Rep> rep = r; + + rep->checkConsistency(); + + int thisSize = rep->size(); + int thisOffset = rep->offset; + int tSize = static_cast<int>(strlen(t)); + int length = thisSize + tSize; + + // possible cases: + if (thisSize == 0) { + // this is empty + rep = createRep(t); + } else if (tSize == 0) { + // t is empty, we'll just return *this below. + } else if (rep->baseIsSelf() && rep->rc == 1) { + // this is direct and has refcount of 1 (so we can just alter it directly) + expandCapacity(rep.get(), thisOffset + length); + UChar* d = rep->data(); + if (d) { + for (int i = 0; i < tSize; ++i) + d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend + rep->len = length; + rep->_hash = 0; + } + } else if (thisOffset + thisSize == rep->baseString->usedCapacity && thisSize >= minShareSize) { + // this string reaches the end of the buffer - extend it + expandCapacity(rep.get(), thisOffset + length); + UChar* d = rep->data(); + if (d) { + for (int i = 0; i < tSize; ++i) + d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend + rep = UString::Rep::create(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) + rep = &UString::Rep::null; + else { + copyChars(d, rep->data(), thisSize); + for (int i = 0; i < tSize; ++i) + d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend + rep = UString::Rep::create(d, length); + rep->capacity = newCapacity; + } + } + + rep->checkConsistency(); -UString::UString(const UString &a, const UString &b) + return rep.release(); +} + +PassRefPtr<UString::Rep> concatenate(UString::Rep* a, UString::Rep* 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; + a->checkConsistency(); + b->checkConsistency(); + + int aSize = a->size(); + int aOffset = a->offset; + int bSize = b->size(); + int bOffset = b->offset; + int length = aSize + bSize; + + // possible cases: - // possible cases: - - if (aSize == 0) { // a is empty - m_rep = b.m_rep; - } else if (bSize == 0) { + if (aSize == 0) + return b; // 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 { + if (bSize == 0) + return a; + + if (bSize == 1 && aOffset + aSize == a->baseString->usedCapacity && aOffset + length <= a->baseString->capacity) { + // b is a single character (common fast case) + a->baseString->usedCapacity = aOffset + length; + a->data()[aSize] = b->data()[0]; + return UString::Rep::create(a, 0, length); + } + + if (aOffset + aSize == a->baseString->usedCapacity && aSize >= minShareSize && 4 * aSize >= bSize && + (-bOffset != b->baseString->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()) + return 0; + copyChars(a->data() + aSize, b->data(), bSize); + PassRefPtr<UString::Rep> result = UString::Rep::create(a, 0, length); + + a->checkConsistency(); + b->checkConsistency(); + result->checkConsistency(); + + return result; + } + + if (-bOffset == b->baseString->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()) + return 0; + copyChars(b->data() - aSize, a->data(), aSize); + PassRefPtr<UString::Rep> result = UString::Rep::create(b, -aSize, length); + + a->checkConsistency(); + b->checkConsistency(); + result->checkConsistency(); + + return result; + } + // 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; + return 0; + copyChars(d, a->data(), aSize); + copyChars(d + aSize, b->data(), bSize); + PassRefPtr<UString::Rep> result = UString::Rep::create(d, length); + result->capacity = newCapacity; + + a->checkConsistency(); + b->checkConsistency(); + result->checkConsistency(); + + return result; +} + +PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, 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 concatenate(rep, minBuf); + } else { + bool negative = false; + if (i < 0) { + negative = true; + i = -i; + } + while (i) { + *--p = static_cast<unsigned short>((i % 10) + '0'); + i /= 10; + } + if (negative) + *--p = '-'; + } + + return concatenate(rep, p, static_cast<int>(end - p)); + +} + +PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, double d) +{ + // avoid ever printing -NaN, in JS conceptually there is only one NaN value + if (isnan(d)) + return concatenate(rep, "NaN"); + + if (d == 0.0) // stringify -0 as 0 + d = 0.0; + + char buf[80]; + int decimalPoint; + int sign; + + char* result = dtoa(d, 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 { - 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; + 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'; } - } + + freedtoa(result); + + return concatenate(rep, buf); } const UString& UString::null() { - static UString* n = new UString; - return *n; + static UString* n = new UString; // Should be called from main thread at least once to be safely initialized. + return *n; } UString UString::from(int i) { - UChar buf[1 + sizeof(i) * 3]; - UChar *end = buf + sizeof(buf) / sizeof(UChar); - UChar *p = end; + 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 = '-'; + 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 = static_cast<unsigned short>((i % 10) + '0'); + i /= 10; + } + if (negative) + *--p = '-'; } - } - - return UString(p, static_cast<int>(end - 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; + UChar buf[sizeof(u) * 3]; + UChar* end = buf + sizeof(buf) / sizeof(UChar); + UChar* p = end; + + if (u == 0) + *--p = '0'; + else { + while (u) { + *--p = static_cast<unsigned short>((u % 10) + '0'); + u /= 10; + } } - } - - return UString(p, static_cast<int>(end - p)); + + 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 = '-'; + 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 = static_cast<unsigned short>((l % 10) + '0'); + l /= 10; + } + if (negative) + *--p = '-'; } - } - - return UString(p, static_cast<int>(end - 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"; + // 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)); + char buf[80]; + int decimalPoint; + int sign; + + char* result = dtoa(d, 0, &decimalPoint, &sign, NULL); + int length = static_cast<int>(strlen(result)); - int i = 0; - if (sign) { - buf[i++] = '-'; - } + 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++) { + if (decimalPoint <= 0 && decimalPoint > -6) { 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++] = '.'; + 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'; } - 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); - + 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; + m_rep->checkConsistency(); + + 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)); } - } 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; + + 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) { + copyChars(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length); + bufferPos += substringRanges[i].length; + } + if (i < separatorCount) { + copyChars(buffer + bufferPos, separators[i].data(), separators[i].size()); + bufferPos += separators[i].size(); + } } - } - return *this; + return UString::Rep::create(buffer, totalLength); } -UString &UString::append(unsigned short c) +UString& UString::append(const UString &t) { - int thisOffset = m_rep->offset; - int length = size(); + m_rep->checkConsistency(); + t.rep()->checkConsistency(); - // 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); + 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()) { + copyChars(m_rep->data() + thisSize, t.data(), tSize); + 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()) { + copyChars(m_rep->data() + thisSize, t.data(), tSize); + 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) + makeNull(); + else { + copyChars(d, data(), thisSize); + copyChars(d + thisSize, t.data(), tSize); + m_rep = Rep::create(d, length); + m_rep->capacity = newCapacity; + } } - } 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; + + m_rep->checkConsistency(); + t.rep()->checkConsistency(); + + return *this; +} + +UString& UString::append(const UChar* tData, int tSize) +{ + m_rep = concatenate(m_rep.release(), tData, tSize); + return *this; +} + +UString& UString::append(const char* t) +{ + m_rep = concatenate(m_rep.release(), t); + return *this; +} + +UString& UString::append(UChar c) +{ + m_rep->checkConsistency(); + + 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) + makeNull(); + 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 = m_rep->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 = m_rep->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) + makeNull(); + else { + copyChars(d, data(), length); + d[length] = c; + m_rep = Rep::create(d, length + 1); + m_rep->capacity = newCapacity; + } } - } - return *this; + m_rep->checkConsistency(); + + return *this; } -CString UString::cstring() const +bool UString::getCString(CStringBuffer& buffer) const { - return ascii(); + int length = size(); + int neededSize = length + 1; + buffer.resize(neededSize); + char* buf = buffer.data(); + + UChar ored = 0; + const UChar* p = data(); + char* q = buf; + const UChar* limit = p + length; + while (p != limit) { + UChar c = p[0]; + ored |= c; + *q = static_cast<char>(c); + ++p; + ++q; + } + *q = '\0'; + + return !(ored & 0xFF00); } -char *UString::ascii() const +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'; + int length = size(); + int neededSize = length + 1; + delete[] statBuffer; + statBuffer = new char[neededSize]; + + const UChar* p = data(); + char* q = statBuffer; + const UChar* limit = p + length; + while (p != limit) { + *q = static_cast<char>(p[0]); + ++p; + ++q; + } + *q = '\0'; - return statBuffer; + return statBuffer; } -UString &UString::operator=(const char *c) +UString& UString::operator=(const char* c) { if (!c) { m_rep = &Rep::null; @@ -900,386 +1194,445 @@ UString &UString::operator=(const char *c) 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; + 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) { + makeNull(); + return *this; + } + m_rep = Rep::create(d, l); } - m_rep = Rep::create(d, l); - } - for (int i = 0; i < l; i++) - d[i].uc = c[i]; + for (int i = 0; i < l; i++) + d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend - return *this; + 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; - } + const UChar* u = data(); + const UChar* limit = u + size(); + while (u < limit) { + if (u[0] > 0xFF) + return false; + ++u; + } - return true; + return true; } -const UChar UString::operator[](int pos) const +UChar UString::operator[](int pos) const { - if (pos >= size()) - return '\0'; - return data()[pos]; + 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 (size() == 1) { + UChar c = data()[0]; + if (isASCIIDigit(c)) + return c - '0'; + if (isASCIISpace(c) && tolerateEmptyString) + return 0; + return NaN; } - 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; + // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk + // after the number, so this is too strict a check. + CStringBuffer s; + if (!getCString(s)) + return NaN; + const char* c = s.data(); - if (*c == '+') + // skip leading white space + while (isASCIISpace(*c)) c++; - else if (*c == '-') { - sign = -1.0; + + // empty string ? + if (*c == '\0') + return tolerateEmptyString ? 0.0 : NaN; + + double d; + + // hex number ? + if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) { + const char* firstDigitPosition = c + 2; 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; + 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 = 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; + // allow trailing white space + while (isASCIISpace(*c)) + c++; + // don't allow anything after - unless tolerant=true + if (!tolerateTrailingJunk && *c != '\0') + d = NaN; - return d; + return d; } double UString::toDouble(bool tolerateTrailingJunk) const { - return toDouble(tolerateTrailingJunk, true); + return toDouble(tolerateTrailingJunk, true); } double UString::toDouble() const { - return toDouble(false, true); + return toDouble(false, true); } -uint32_t UString::toUInt32(bool *ok) const +uint32_t UString::toUInt32(bool* ok) const { - double d = toDouble(); - bool b = true; + double d = toDouble(); + bool b = true; - if (d != static_cast<uint32_t>(d)) { - b = false; - d = 0; - } + if (d != static_cast<uint32_t>(d)) { + b = false; + d = 0; + } - if (ok) - *ok = b; + if (ok) + *ok = b; - return static_cast<uint32_t>(d); + return static_cast<uint32_t>(d); } -uint32_t UString::toUInt32(bool *ok, bool tolerateEmptyString) const +uint32_t UString::toUInt32(bool* ok, bool tolerateEmptyString) const { - double d = toDouble(false, tolerateEmptyString); - bool b = true; + double d = toDouble(false, tolerateEmptyString); + bool b = true; - if (d != static_cast<uint32_t>(d)) { - b = false; - d = 0; - } + if (d != static_cast<uint32_t>(d)) { + b = false; + d = 0; + } - if (ok) - *ok = b; + if (ok) + *ok = b; - return static_cast<uint32_t>(d); + return static_cast<uint32_t>(d); } -uint32_t UString::toStrictUInt32(bool *ok) const +uint32_t UString::toStrictUInt32(bool* ok) const { - if (ok) - *ok = false; + 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(); + // 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[0]; - // 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) + // If the first digit is 0, only 0 itself is OK. + if (c == '0') { + if (len == 1 && ok) + *ok = true; return 0; - i += d; - - // Handle end of string. - if (--len == 0) { - if (ok) - *ok = true; - return i; } - - // Get next character. - c = (++p)->unicode(); - } + + // 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); + } } -int UString::find(const UString &f, int pos) const +int UString::find(const UString& f, int pos) const { - int sz = size(); - int fsz = f.size(); - if (sz < fsz) + 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[0]; + ++fdata; + for (const UChar* c = data() + pos; c <= end; c++) { + if (c[0] == fchar && !memcmp(c + 1, fdata, fsizeminusone)) + return static_cast<int>(c - data()); + } + 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; + 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 UString::rfind(const UString& f, int pos) const { - int sz = size(); - int fsz = f.size(); - if (sz < fsz) + 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; - 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()); - } + 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; + 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; + 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)); + 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); + int size = s1.size(); + switch (size) { + case 0: + return !s2.size(); + case 1: + return s2.size() == 1 && s1.data()[0] == s2.data()[0]; + default: + return s2.size() == size && memcmp(s1.data(), s2.data(), size * 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++; - } + if (s2 == 0) + return s1.isEmpty(); + + const UChar* u = s1.data(); + const UChar* uend = u + s1.size(); + while (u != uend && *s2) { + if (u[0] != (unsigned char)*s2) + return false; + s2++; + u++; + } - return u == uend && *s2 == 0; + 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); + 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[0] < c2[0]); - return (l1 < l2); + return (l1 < l2); +} + +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[0] > c2[0]); + + 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++; - } + 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 (l < lmin) + return (c1[0] > c2[0]) ? 1 : -1; + + if (l1 == l2) + return 0; - if (l1 == l2) - return 0; + return (l1 > l2) ? 1 : -1; +} - return (l1 > l2) ? 1 : -1; +bool 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] != s[i]) + return false; + } + return true; } 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); + // 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()[0]); + ConversionResult result = convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), strict); + if (result != conversionOK) + return CString(); + + return CString(buffer.data(), p - buffer.data()); +} - // 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(); +// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X. +NEVER_INLINE void UString::makeNull() +{ + m_rep = &Rep::null; +} - return CString(buffer.data(), p - buffer.data()); +// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X. +NEVER_INLINE UString::Rep* UString::nullRep() +{ + return &Rep::null; } -} // namespace KJS +} // namespace JSC diff --git a/JavaScriptCore/kjs/ustring.h b/JavaScriptCore/kjs/ustring.h index 7faa86a..f47b134 100644 --- a/JavaScriptCore/kjs/ustring.h +++ b/JavaScriptCore/kjs/ustring.h @@ -1,7 +1,6 @@ -// -*- 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) 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 @@ -23,448 +22,364 @@ #ifndef _KJS_USTRING_H_ #define _KJS_USTRING_H_ -#include "JSLock.h" #include "collector.h" #include <stdint.h> +#include <string.h> #include <wtf/Assertions.h> #include <wtf/FastMalloc.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> +#include <wtf/unicode/Unicode.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 +namespace JSC { -/** - * @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; + using WTF::PlacementNewAdoptType; + using WTF::PlacementNewAdopt; + + class IdentifierTable; + + class CString { + public: + CString() + : m_length(0) + , m_data(0) + { + } + + CString(const char*); + CString(const char*, size_t); + CString(const CString&); + + ~CString(); + + static CString adopt(char*, size_t); // buffer should be allocated with new[]. + + 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 m_length; } + const char* c_str() const { return m_data; } + + private: + size_t m_length; + char* m_data; }; - 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 { + typedef Vector<char, 32> CStringBuffer; + + class UString { + friend class CTI; + public: - Range(int pos, int len) : position(pos), length(len) {} - Range() {} - int position; - int length; + struct Rep { + friend class CTI; + + static PassRefPtr<Rep> create(UChar*, int); + static PassRefPtr<Rep> createCopying(const UChar*, int); + static PassRefPtr<Rep> create(PassRefPtr<Rep> base, int offset, int length); + + // Constructs a string from a UTF-8 string, using strict conversion (see comments in UTF8.h). + // Returns UString::Rep::null for null input or conversion failure. + static PassRefPtr<Rep> createFromUTF8(const char*); + + 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*, int length); + static unsigned computeHash(const char* s) { return computeHash(s, strlen(s)); } + + IdentifierTable* identifierTable() const { return reinterpret_cast<IdentifierTable*>(m_identifierTable & ~static_cast<uintptr_t>(1)); } + void setIdentifierTable(IdentifierTable* table) { ASSERT(!isStatic()); m_identifierTable = reinterpret_cast<intptr_t>(table); } + + bool isStatic() const { return m_identifierTable & 1; } + void setStatic(bool v) { ASSERT(!identifierTable()); m_identifierTable = v; } + + Rep* ref() { ++rc; return this; } + ALWAYS_INLINE void deref() { if (--rc == 0) destroy(); } + + void checkConsistency() const; + + // unshared data + int offset; + int len; + int rc; // For null and empty static strings, this field does not reflect a correct count, because ref/deref are not thread-safe. A special case in destroy() guarantees that these do not get deleted. + mutable unsigned _hash; + intptr_t m_identifierTable; // A pointer to identifier table. The lowest bit is used to indicate whether the string is static (null or empty). + UString::Rep* baseString; + size_t reportedCost; + + // potentially shared data. 0 if backed up by a base string. + UChar* buf; + int usedCapacity; + int capacity; + int usedPreCapacity; + int preCapacity; + + static Rep null; + static Rep empty; + }; + + public: + UString(); + UString(const char*); + UString(const UChar*, int length); + UString(UChar*, int length, bool copy); + + UString(const UString& s) + : m_rep(s.m_rep) + { + } + + UString(const Vector<UChar>& buffer); + + ~UString() + { + } + + // Special constructor for cases where we overwrite an object in place. + UString(PlacementNewAdoptType) + : m_rep(PlacementNewAdopt) + { + } + + static UString from(int); + static UString from(unsigned int); + static UString from(long); + static UString from(double); + + 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; + + UString& append(const UString&); + UString& append(const char*); + UString& append(UChar); + UString& append(char c) { return append(static_cast<UChar>(static_cast<unsigned char>(c))); } + UString& append(const UChar*, int size); + + bool getCString(CStringBuffer&) const; + + // NOTE: This method should only be used for *debugging* purposes as it + // is neither Unicode safe nor free from side effects nor thread-safe. + 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; + + UString& operator=(const char*c); + + UString& operator+=(const UString& s) { return append(s); } + UString& operator+=(const char* s) { return append(s); } + + const UChar* data() const { return m_rep->data(); } + + bool isNull() const { return (m_rep == &Rep::null); } + bool isEmpty() const { return (!m_rep->len); } + + bool is8Bit() const; + + int size() const { return m_rep->size(); } + + UChar operator[](int pos) const; + + double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const; + double toDouble(bool tolerateTrailingJunk) const; + double toDouble() const; + + uint32_t toUInt32(bool* ok = 0) const; + uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const; + uint32_t toStrictUInt32(bool* ok = 0) const; + + unsigned toArrayIndex(bool* ok = 0) const; + + int find(const UString& f, int pos = 0) const; + int find(UChar, int pos = 0) const; + int rfind(const UString& f, int pos) const; + int rfind(UChar, int pos) const; + + UString substr(int pos = 0, int len = -1) const; + + static const UString& null(); + + Rep* rep() const { return m_rep.get(); } + static Rep* nullRep(); + + UString(PassRefPtr<Rep> r) + : m_rep(r) + { + ASSERT(m_rep); + } + + size_t cost() const; + + private: + int usedCapacity() const; + int usedPreCapacity() const; + void expandCapacity(int requiredLength); + void expandPreCapacity(int requiredPreCap); + void makeNull(); + + RefPtr<Rep> m_rep; + + friend bool operator==(const UString&, const UString&); + friend PassRefPtr<Rep> concatenate(Rep*, Rep*); // returns 0 if out of memory }; + PassRefPtr<UString::Rep> concatenate(UString::Rep*, UString::Rep*); + PassRefPtr<UString::Rep> concatenate(UString::Rep*, int); + PassRefPtr<UString::Rep> concatenate(UString::Rep*, double); - 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 + bool operator==(const UString&, const UString&); + + inline bool operator!=(const UString& s1, const UString& s2) + { + return !JSC::operator==(s1, s2); + } + + bool operator<(const UString& s1, const UString& s2); + bool operator>(const UString& s1, const UString& s2); + + bool operator==(const UString& s1, const char* s2); + + inline bool operator!=(const UString& s1, const char* s2) + { + return !JSC::operator==(s1, s2); + } + + inline bool operator==(const char *s1, const UString& s2) + { + return operator==(s2, s1); + } + + inline bool operator!=(const char *s1, const UString& s2) + { + return !JSC::operator==(s1, s2); + } + + bool operator==(const CString&, const CString&); + + inline UString operator+(const UString& s1, const UString& s2) + { + RefPtr<UString::Rep> result = concatenate(s1.rep(), s2.rep()); + return UString(result ? result.release() : UString::nullRep()); + } + + int compare(const UString&, const UString&); + + bool equal(const UString::Rep*, const UString::Rep*); + +#ifdef NDEBUG + inline void UString::Rep::checkConsistency() const + { + } +#endif + + 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 = Heap::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; + } + + struct IdentifierRepHash : PtrHash<RefPtr<JSC::UString::Rep> > { + static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->computedHash(); } + static unsigned hash(JSC::UString::Rep* key) { return key->computedHash(); } + }; + +} // namespace JSC + +namespace WTF { + + template<typename T> struct DefaultHash; + template<typename T> struct StrHash; + + template<> struct StrHash<JSC::UString::Rep*> { + static unsigned hash(const JSC::UString::Rep* key) { return key->hash(); } + static bool equal(const JSC::UString::Rep* a, const JSC::UString::Rep* b) { return JSC::equal(a, b); } + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + template<> struct StrHash<RefPtr<JSC::UString::Rep> > : public StrHash<JSC::UString::Rep*> { + using StrHash<JSC::UString::Rep*>::hash; + static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->hash(); } + using StrHash<JSC::UString::Rep*>::equal; + static bool equal(const RefPtr<JSC::UString::Rep>& a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a.get(), b.get()); } + static bool equal(const JSC::UString::Rep* a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a, b.get()); } + static bool equal(const RefPtr<JSC::UString::Rep>& a, const JSC::UString::Rep* b) { return JSC::equal(a.get(), b); } + + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + template<> struct DefaultHash<JSC::UString::Rep*> { + typedef StrHash<JSC::UString::Rep*> Hash; + }; + + template<> struct DefaultHash<RefPtr<JSC::UString::Rep> > { + typedef StrHash<RefPtr<JSC::UString::Rep> > Hash; + + }; + +} // namespace WTF #endif diff --git a/JavaScriptCore/kjs/value.cpp b/JavaScriptCore/kjs/value.cpp deleted file mode 100644 index 55da40b..0000000 --- a/JavaScriptCore/kjs/value.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5ebb575..0000000 --- a/JavaScriptCore/kjs/value.h +++ /dev/null @@ -1,523 +0,0 @@ -/* - * 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 |