summaryrefslogtreecommitdiffstats
path: root/kjs
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kjs
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kjs')
-rw-r--r--kjs/ChangeLog432
-rw-r--r--kjs/DESIGN.ideas110
-rw-r--r--kjs/Mainpage.dox30
-rw-r--r--kjs/Makefile.am108
-rw-r--r--kjs/README27
-rw-r--r--kjs/THANKS7
-rw-r--r--kjs/array_instance.h71
-rw-r--r--kjs/array_object.cpp869
-rw-r--r--kjs/array_object.h67
-rw-r--r--kjs/bool_object.cpp146
-rw-r--r--kjs/bool_object.h90
-rw-r--r--kjs/collector.cpp312
-rw-r--r--kjs/collector.h74
-rw-r--r--kjs/completion.h69
-rw-r--r--kjs/configure.in.in93
-rw-r--r--kjs/context.h90
-rwxr-xr-xkjs/create_hash_table203
-rw-r--r--kjs/date_object.cpp1214
-rw-r--r--kjs/date_object.h127
-rw-r--r--kjs/debugger.cpp135
-rw-r--r--kjs/debugger.h210
-rw-r--r--kjs/dtoa.cpp3319
-rw-r--r--kjs/dtoa.h31
-rw-r--r--kjs/error_object.cpp192
-rw-r--r--kjs/error_object.h96
-rw-r--r--kjs/function.cpp1046
-rw-r--r--kjs/function.h65
-rw-r--r--kjs/function_object.cpp315
-rw-r--r--kjs/function_object.h81
-rw-r--r--kjs/global.h.in54
-rw-r--r--kjs/grammar.cpp3139
-rw-r--r--kjs/grammar.h215
-rw-r--r--kjs/grammar.y699
-rw-r--r--kjs/identifier.cpp308
-rw-r--r--kjs/identifier.h155
-rw-r--r--kjs/internal.cpp1088
-rw-r--r--kjs/internal.h508
-rw-r--r--kjs/interpreter.cpp413
-rw-r--r--kjs/interpreter.h498
-rw-r--r--kjs/keywords.table70
-rw-r--r--kjs/kjs-devel-gdb22
-rw-r--r--kjs/lexer.cpp930
-rw-r--r--kjs/lexer.h165
-rw-r--r--kjs/libkjs.map30
-rw-r--r--kjs/list.cpp328
-rw-r--r--kjs/list.h207
-rw-r--r--kjs/lookup.cpp114
-rw-r--r--kjs/lookup.h401
-rw-r--r--kjs/math_object.cpp268
-rw-r--r--kjs/math_object.h54
-rw-r--r--kjs/nodes.cpp3137
-rw-r--r--kjs/nodes.h1082
-rw-r--r--kjs/nodes2string.cpp629
-rw-r--r--kjs/number_object.cpp512
-rw-r--r--kjs/number_object.h99
-rw-r--r--kjs/object.cpp563
-rw-r--r--kjs/object.h726
-rw-r--r--kjs/object_object.cpp204
-rw-r--r--kjs/object_object.h84
-rw-r--r--kjs/operations.cpp265
-rw-r--r--kjs/operations.h79
-rw-r--r--kjs/property_map.cpp656
-rw-r--r--kjs/property_map.h107
-rw-r--r--kjs/reference.cpp193
-rw-r--r--kjs/reference.h89
-rw-r--r--kjs/reference_list.cpp154
-rw-r--r--kjs/reference_list.h75
-rw-r--r--kjs/regexp.cpp443
-rw-r--r--kjs/regexp.h97
-rw-r--r--kjs/regexp_object.cpp378
-rw-r--r--kjs/regexp_object.h97
-rw-r--r--kjs/scope_chain.cpp89
-rw-r--r--kjs/scope_chain.h79
-rw-r--r--kjs/simple_number.h52
-rw-r--r--kjs/string_object.cpp701
-rw-r--r--kjs/string_object.h119
-rw-r--r--kjs/test.js29
-rw-r--r--kjs/testkjs.cpp173
-rw-r--r--kjs/types.h25
-rw-r--r--kjs/ustring.cpp983
-rw-r--r--kjs/ustring.h476
-rw-r--r--kjs/value.cpp412
-rw-r--r--kjs/value.h400
83 files changed, 32502 insertions, 0 deletions
diff --git a/kjs/ChangeLog b/kjs/ChangeLog
new file mode 100644
index 000000000..c5f42ace2
--- /dev/null
+++ b/kjs/ChangeLog
@@ -0,0 +1,432 @@
+2007-04-20 Harri Porten <porten@kde.org>
+
+ * number_object.cpp: fixed leading-zero loss on toFixed() call by
+ following the spec algorithm properly. Nikolay Pavlov's bug
+ report: http://bugs.kde.org/144115.
+
+2007-03-13 Harri Porten <porten@kde.org>
+
+ * function.cpp (decodeURI): don't drop last character of unescaped
+ (reserved) sequences.
+
+2007-02-24 Harri Porten <porten@kde.org>
+
+ * regexp.cpp: gracefully handle incomplete \u sequences in regular
+ expressions the IE way. Fixes the syntax error reported in bug
+ #141731 although the page might be relying on the equally
+ undefined FF way.
+
+2007-02-23 Harri Porten <porten@kde.org>
+
+ * number_object.cpp: applied patch by Justin that fixes
+ toExponential() calls on negative numbers. Bug #141979.
+
+ * function.cpp: implemented caller property as requested by
+ bug report #132039 from Edward Rudd.
+
+2007-02-22 Harri Porten <porten@kde.org>
+
+ * keywords.table: unreserve "class" keyword as it is for FF >=
+ 1.5. Frequent source of errors.
+
+2007-02-20 Harri Porten <porten@kde.org>
+
+ * string_object.cpp: basic String localeCompare() function
+
+2007-02-17 Harri Porten <porten@kde.org>
+
+ * lexer.cpp: parse code with null characters in them
+
+2007-02-10 Harri Porten <porten@kde.org>
+
+ * lexer.cpp: support named function expressions
+
+2006-11-04 Harri Porten <porten@kde.org>
+
+ * number_object.cpp: Fixed toFixed(n) calls on negative zero.
+
+ * number_object.cpp: Fixed toPrecision(n) calls on 1.0.
+
+2005-09-17 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: MSVC equivalent for strncasecmp().
+
+2005-07-26 Harri Porten <porten@kde.org>
+
+ * array_object.cpp: the to*String() logic was more involved
+ that I originally thought. Fixes bugs of our and the JSC
+ implementation.
+
+2005-07-24 Harri Porten <porten@kde.org>
+
+ * array_object.cpp: harmonized toLocaleString implementation
+ with JSC.
+
+ * date_object.cpp: applied prototype inheritance fixed from JSC.
+ Makes Mozilla's ecma/Date/15.9.5.js test pass.
+
+ * nodes.cpp: the return-outside-of-function check got duplicated
+ in JSC. Harmonize them.
+
+2005-06-22 Harri Porten <porten@kde.org>
+
+ * function.cpp: pre-process var declaration in eval()
+
+ * nodes.cpp: picked up "const" support improvment from JSC
+
+ * regexp_object.cpp (construct): don't misinterpret an undefined
+ flags parameter.
+
+2005-06-21 Harri Porten <porten@kde.org>
+
+ * string_object.cpp: fixed length properties of indexOf() and
+ lastIndexOf().
+
+2005-06-20 Harri Porten <porten@kde.org>
+
+ * nodes.cpp: JSC patch that helps setting exception details (line
+ number) where they had been missing before.
+
+2005-06-18 Harri Porten <porten@kde.org>
+
+ * regexp_object.cpp: some regexp property fixes from JSC
+
+ * regexp_object.cpp: fixed RegExp.prototype name
+
+ * regexp_object.cpp: allow RegExp.prototype.toString() on the
+ prototype itself (patch from JSC).
+
+ * array_object.cpp: also do Array length check in constructor like
+ JSC does.
+
+ * math_object.cpp: fix for Math.max() handling of negative zero
+ from JSC.
+
+2005-06-16 Harri Porten <porten@kde.org>
+
+ * nodes.cpp: fixed side effect of const declarations on for(;;)
+ variable declarations.
+
+2005-06-11 Harri Porten <porten@kde.org>
+
+ * date_object.cpp (KRFCDate_parseDate): parse AM and PM. Fixed
+ version of JavaScriptCore patch.
+ (KRFCDate_parseDate): fix time zone parsing
+ that broke due to a compiler warning fix in 2003 (r275385).
+ (KRFCDate_parseDate): case insensitive parsing of GMT and UTC
+
+2005-05-28 Harri Porten <porten@kde.org>
+
+ * nodes.cpp: fixed override of properties on variable
+ declarations. See bug report #104181.
+
+2005-05-16 Harri Porten <porten@kde.org>
+
+ * removed remaining use of deprecated Value::isNull().
+
+2005-04-24 Harri Porten <porten@kde.org>
+
+ * lexer.cpp (isIdentLetter): allow umlauts, accents as well as
+ greek, cyrillic, thai etc. letters in identifier names.
+
+ * date_object.cpp (KRFCDate_parseDate): correctly handle large
+ year numbers in "MM/DD/YYYY" formats
+
+ * date_object.cpp (KRFCDate_parseDate): parse date strings that
+ have no time but a timezone. Like "3/31/2005 GMT".
+
+2005-04-17 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: parse date strings like "Apr17,2005"
+
+ * function.cpp: don't produce a null string result on unescape("")
+ (spotted in the JSC ChangeLog)
+
+ * string_object.cpp: faking toLocalLowerCase and toLocalUpperCase
+ by using their non-localized counter parts
+
+2005-04-16 Harri Porten <porten@kde.org>
+
+ * function.cpp: escape() u0000 properly (found in JSC)
+
+ * nodes.cpp: save some cpu cycles on variable declarations
+
+ * error_object.cpp: made prototype read-only (JSC patch)
+
+ * string_object.cpp: adopted tag casing and code formatting to JSC-style
+
+2005-02-27 Harri Porten <porten@kde.org>
+
+ * regexp_object.cpp: fixed RegExp(/regexp/) constructor
+
+ * regexp_object.cpp: throw exception on invalid regexps
+
+2004-11-07 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: fix conversion of Date(value) argument, fixed
+ getDay() for out-of-normal-range dates
+
+2004-10-13 Harri Porten <porten@kde.org>
+
+ * regexp.cpp: support \u escape sequences in regular expressions
+
+2004-10-11 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: make the Date object work outside of the
+ typical Unix range (1900-2038) by shifting other dates into this
+ range. Might still have some bugs with e.g. leap days but this is
+ a big step forward to ECMA compliancy.
+
+ * date_object.cpp: fixed cut-off date in Date.setYear()
+
+2004-10-02 Harri Porten <porten@kde.org>
+
+ * lexer.cpp: parse function expressions with identifier as
+ function argument
+
+ * date_object.cpp: parse YYYY/MM/DD-style dates
+
+2004-09-30 Harri Porten <porten@kde.org>
+
+ * math_object.cpp: fixed Math.round() for very large numbers (bug
+ discovered by Pascal) and negative numbers with a .5 decimal.
+
+2004-09-29 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: don't preset DST when changing parts of the
+ date (most notably the month). Patch by Pascal Letourneau.
+
+2004-06-08 Harri Porten <porten@kde.org>
+
+ * regexp.cpp (RegExp): check regcomp's return value in non-PCRE
+ builds. Invalid regexps are otherwise reported to cause crashes by
+ Luciano Montanaro.
+
+2004-05-11 Harri Porten <porten@kde.org>
+
+ * nodes.cpp (processVarDecls): corrected 03-20 change. Non-eval
+ variable declarations always have the DontDelete attribute set.
+
+2004-04-25 Ian Reinhart Geiser <geiseri@kde.org>
+
+ * Make Math.random() more random by seeding the generator
+ with the system time.
+
+2004-03-20 Harri Porten <porten@kde.org>
+
+ * proper support for JavaScript 1.5 "const" keyword
+
+2004-02-23 Harri Porten <porten@kde.org>
+
+ * keywords.table: for now, make "const" a synonym for "var". Not
+ standardized as of Edition 3 but already supported by other
+ browsers.
+
+2004-02-22 Harri Porten <porten@kde.org>
+
+ * number_object.cpp: fixed crash if toString() is called on NaN
+ or Inf with a radix != 10.
+
+ * error_object.cpp: Error constructors are of [[Class]] Function
+ while Error instances are of [[Class]] Error.
+
+2004-02-21 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: introduced invalidDate constant. Stricter
+ verification of month names. Both coming from JavaScriptCore.
+
+2004-01-25 Harri Porten <porten@kde.org>
+
+ * nodes.cpp: better error messages when property access fails
+ because of null or undefined reference. In debug builds only
+ to not cause a speed impact.
+
+2003-12-29 Dirk Mueller <mueller@kde.org>
+
+ * create_hash_table: implement string table to reduce amount
+ of relocations and memory usage.
+ * lookup.cpp/.h: adjust.
+
+2003-12-10 Stephan Kulow <coolo@kde.org>
+
+ * kjs/ustring.cpp (UString::toDouble): Separate the "tolerant"
+ parameter into two separate ones: tolerateTrailingJunk and
+ tolerateEmptyString (patch by Apple)
+
+2003-11-25 David Faure <faure@kde.org>
+
+ * regexp_object.cpp (construct): Add check for (regexp,flags) case
+ and throw TypeError exception in that case, as specified in 15.10.4.1.
+
+2003-11-21 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: return "Invalid Date" on string conversions of
+ NaN dates (patch by Apple)
+
+2003-11-20 Harri Porten <porten@kde.org>
+
+ * date_object.cpp: return NaN in getter functions if the time
+ value is NaN itself.
+
+2003-11-18 Harri Porten <porten@kde.org>
+
+ * reference_list.cpp: patch by Maciej that initializes
+ ReferenceListHeadNode's length field
+
+2003-11-17 Harri Porten <porten@kde.org>
+
+ * string_object.cpp: handle negative slice() arguments correctly
+
+ * function_object.cpp: fixed bracketing to ensure null check is done
+
+2003-11-17 Harri Porten <harri@froglogic.com>
+
+ * internal.cpp (evaluate): lexical error means parse error
+
+ * lexer.cpp: removed stderr debug output
+
+ * object.h: renamed virtual get(), put(), hasProperty() and
+ deleteProperty() overloads accepting an int property to
+ getPropertyByIndex() etc. Not only cleaner C++ that makes
+ compilers happier but also helps to make the code more
+ understandable and safer.
+
+2003-11-16 Harri Porten <harri@froglogic.com>
+
+ * array_object.cpp: fixed range error in Array.slice()
+
+2003-11-11 Harri Porten <porten@kde.org>
+
+ * array_object.cpp: patch from Darin that adds checks for
+ undefined type in compare functions
+
+2003-11-07 Harri Porten <porten@kde.org>
+
+ * grammar.y: do automatic semicolon insertion after throw statements
+
+2003-11-06 Harri Porten <porten@kde.org>
+
+ * adapted patch from Maciej that plugs string leaks on parse errors
+
+2003-11-05 Zack Rusin <zack@kde.org>
+
+ * value.cpp: (operator=): increment reference count on the copying
+ object before dereferencing the currently held value
+
+2003-11-04 David Faure <faure@kde.org>
+
+ * string_object.cpp: (StringProtoFuncImp::call): Don't do an early return, since that
+ could leak a temporary regexp. Patch from Maciej.
+
+2003-11-02 Harri Porten <harri@froglogic.com>
+
+ * nodes.h: list handling fix for CaseClausNode by Darin Adler
+
+ * grammar.y: added CatchNode and FinallyNode types for greater type safety
+
+2003-10-29 Harri Porten <porten@kde.org>
+
+ * object.cpp (call): patch from Maciej that makes us back away
+ from the recursion limit once we have hit it
+
+ * nodes.*: got rid of remaining reverse*() functions
+
+2003-10-26 Harri Porten <porten@kde.org>
+
+ * date_object.cpp (call): respect optional arguments in set*
+ functions.
+
+ * ustring.cpp: more and correct use of memcpy()
+
+ * ustring.*: store length within CString to be able to have
+ null bytes in the string (JavaScriptCore)
+
+ * added Apple's Identifier::toArrayIndex() and use it in
+ ArrayInstanceImp instead of our range validity checks
+
+ * do without ArgumentsNode::reverseList()
+
+2003-10-20 Harri Porten <harri@froglogic.com>
+
+ * number_object.cpp: rewrote Number.toString(radix) to work with
+ negative numbers, floating point and very large numbers.
+
+2003-10-19 Harri Porten <porten@froglogic.com>
+
+ * grammar.y: fixed bitwise XOR and OR expressions
+
+2003-09-30 Harri Porten <harri@froglogic.com>
+
+ * lexer.cpp (isWhiteSpace): allow no-break space
+ * date_object.cpp: only use IE getYear() style if explicitly
+ chosen
+
+================= long break again ================================
+
+2003-02-02 Harri Porten <porten@kde.org>
+
+ * internal.h: added NumberImp::staticNaN
+ * gave PropertyNode and PropertyValueNode their own types in the
+ parser, ref and deref them as list rather then recursively
+ * turned recursive object literal evaluation into a simple loop
+
+2003-02-01 Harri Porten <porten@kde.org>
+
+ * internal.cpp (putValue): throw error if reference is invalid
+ * nodes.cpp (evaluateReference): added to allow (i) = 0;
+
+================= long unlogged time span ================================
+
+2002-06-15 Harri Porten <porten@kde.org>
+
+ * regexp_object.cpp: made RegExp.prototype visible
+ * adjusted length property of slit and split to what the spec says
+ * some conversions and new calls less
+
+2002-06-08 Harri Porten <porten@kde.org>
+
+ * string_object.cpp: fixed the leaks introduced by the previous
+ fixes of match() and split(). One has to clean up after each
+ call to RegExp::match().
+ * added some KJS_DEBUG_MEM ifdef'ed globalClear() functions
+ that clear up static allocations. Makes debugging memory
+ leaks easier as we're down to 0 "still reachable" leaks (apart
+ from STL related issue in the node leak check list).
+
+2002-06-02 Harri Porten <porten@kde.org>
+
+ * math_object.cpp: fixed handling of NaN in Math.round()
+
+2002-06-01 Harri Porten <porten@kde.org>
+
+ * string_object.cpp: correct global flag use in match(). Fixed
+ some split() cases. Fixed unlikely leak.
+ * regexp_object.cpp: set 'index' and 'input' properties of
+ RegExp.prototype.exec() and String.prototype.match() result.
+ Made new RegExp() really produce an empty regexp.
+ * array_object.cpp: correct sorting for 'undefined' properties
+
+2001-01-04 Harri Porten <harri@trolltech.com>
+
+ * ustring.h: pack bytes to avoid alignment problems (ARM) reported
+ by Stefan Hanske <sh990154@mail.uni-greifswald.de>
+ * nodes.cpp: typeof fix by Emmeran Seehuber <the_emmy@gmx.de>
+ * nodes.cpp: fixed order of function declaration proccessing
+
+2000-12-18 Harri Porten <harri@trolltech.com>
+
+ * string_object.cpp: fixed out-of-bounds error in fromCharCode()
+
+2000-12-11 Harri Porten <harri@trolltech.com>
+
+ * regexp.h: compile fix for buggy libc
+ * ustring.cpp: format string conversion of numbers with %g
+
+2000-12-10 Harri Porten <harri@trolltech.com>
+
+ * lexer.cpp: parsing != was broken, added \v escape in strings,
+ fixed "\u" and "\x" and \x with non hex chars following.
+ * nodes.cpp: implemented <<=, >>=, >>>=, &=, ^=, |= and %=
+ * internal.cpp: create error message including line no on parse errors
+
+
diff --git a/kjs/DESIGN.ideas b/kjs/DESIGN.ideas
new file mode 100644
index 000000000..d7684b45b
--- /dev/null
+++ b/kjs/DESIGN.ideas
@@ -0,0 +1,110 @@
+Get rid of SourceElementsNode by integrating its functionality into
+StatementNode.
+
+==========================================================================
+
+The hash value of a string could be calculated at creation time and be
+stored in the UString instance for later use by the lookup functions.
+
+==========================================================================
+
+Proposal for a new object model. Far from being complete.
+
+ Object Type
++---------+ +-------------+
+| type | ----------------> | toString() |
+| | | toNumber() |
+| data | | .... |
++---------+ | construct() |
+ | +-------------+
+ | | /|\
+ \|/ | |
++---------+ | |
+| type | | |
+| | Shared (optional) \|/ |
+| data | +-------------+
++---------+ +---------+ | types |
+ /|\ | |<------| gc | Interpreter/Environment
+ +-------| | | .... |
+ | | | excp state |
+ +---------+ +-------------+
+ Garbage Collector
+
+Features:
+ - offers class static data (nice replacement for pointers to member
+ function objects in the prototype object)
+ - no more need to pass around ExecState pointers for the C++ user
+ (substituted with the need for Object* in the type implementation)
+ - simple types are stored simple (no new'ed Imp objects)
+
+Alternative A: pass around Object by pointer rather than value
+ rather than new'ing they should come out of a pool
+
+Alternative B: instead of virtual functions like toBoolean(), Type could
+ have an array of function pointers which can be modified
+ on the fly and checked for != 0.
+
+Limitations: Konqueror's requirement to allow access to other frame's
+ interpreter data but flagging errors on the caller's side
+ is not satisfied.
+
+class Interpreter;
+
+class Type {
+public:
+ Type(Interpreter* i, Type *b) : ip(i), bs(b) { }
+ virtual UString name() const = 0;
+ Type* base() const { return bs; }
+ Interpreter* interpreter() const { return ip; }
+
+ virtual bool toBoolean(Object *o);
+ // ....
+ virtual Object construct(const List &args);
+
+ // factory
+ Boolean newBoolean(bool b) { return Boolean(interpreter(), b); }
+
+private:
+ Interpreter* ip;
+ Type* bs;
+};
+
+union Data {
+ bool b;
+ double d;
+ // UString ???
+ Shared* sh;
+};
+
+class Object {
+public:
+ // creation
+ Boolean newBoolean(bool b) { return Boolean(typ->interpreter(), b); }
+
+ // conversion
+ bool to Boolean() const { return typ->toBoolean(this); }
+
+ // this object's "parent"
+ Interpreter* interpreter() const { return typ->ip; }
+private:
+ Type* typ;
+ Data dat;
+};
+
+class Boolean : public Object {
+public:
+ // used by convenience function newBoolean()
+ Boolean(Interpreter *i, bool b) {
+ typ = i->booleanType();
+ dat.b = b;
+ }
+ Boolean(const Boolean &b) {
+ typ = b.typ;
+ dat.b = b.b;
+ }
+ Boolean& operator=(const Boolean &b) {
+ type = b.typ;
+ dat.b = b.b;
+ return *this;
+ }
+};
diff --git a/kjs/Mainpage.dox b/kjs/Mainpage.dox
new file mode 100644
index 000000000..e27a193c0
--- /dev/null
+++ b/kjs/Mainpage.dox
@@ -0,0 +1,30 @@
+/** @mainpage KDE JavaScript/EcmaScript Engine
+
+This library provides an ECMAScript compatible interpreter. The ECMA standard
+is based on well known scripting languages such as Netscape's JavaScript and
+Microsoft's JScript.
+
+@authors
+Harri Porten \<porten@kde.org\><br>
+Maks Orlovich \<maksim@kde.org\><br>
+Apple Computer, Inc.<br>
+Richard Moore \<rich@kde.org\><br>
+Daegeun Lee \<realking@mizi.com\><br>
+Marco Pinelli \<pinmc@libero.it\><br>
+Christian Kirsch \<ck@held.mind.de\>
+
+@maintainers
+Maks Orlovich \<maksim@kde.org\><br>
+Harri Porten \<porten@kde.org\>
+
+@licenses
+@lgpl
+
+*/
+
+// DOXYGEN_REFERENCES = kdecore
+// DOXYGEN_SET_PROJECT_NAME = KJS
+// DOXYGEN_SET_EXCLUDE_PATTERNS += */wtf/*
+// DOXYGEN_SET_EXCLUDE_PATTERNS += */kjs/*.cpp */kjs/*.h
+
+// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/kjs/Makefile.am b/kjs/Makefile.am
new file mode 100644
index 000000000..59fe7e855
--- /dev/null
+++ b/kjs/Makefile.am
@@ -0,0 +1,108 @@
+# This file is part of the KDE libraries
+# Copyright (C) 1999 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; see the file COPYING.LIB. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+YACC = bison
+INCLUDES = $(PCRECFLAGS) $(all_includes)
+
+lib_LTLIBRARIES = libkjs.la
+
+libkjs_la_SOURCES = ustring.cpp date_object.cpp collector.cpp nodes.cpp \
+ grammar.cpp lexer.cpp lookup.cpp operations.cpp \
+ regexp.cpp function_object.cpp string_object.cpp \
+ bool_object.cpp number_object.cpp internal.cpp \
+ array_object.cpp math_object.cpp object_object.cpp \
+ regexp_object.cpp error_object.cpp function.cpp \
+ debugger.cpp value.cpp list.cpp object.cpp \
+ interpreter.cpp property_map.cpp nodes2string.cpp \
+ identifier.cpp reference.cpp reference_list.cpp \
+ scope_chain.cpp dtoa.cpp
+
+kjsincludedir = $(includedir)/kjs
+kjsinclude_HEADERS = value.h types.h object.h interpreter.h operations.h \
+ ustring.h lookup.h global.h identifier.h property_map.h \
+ reference.h reference_list.h completion.h scope_chain.h \
+ list.h simple_number.h function.h
+
+noinst_HEADERS = internal.h collector.h nodes.h lexer.h lexer.lut.h \
+ grammar.h regexp.cpp function_object.h string_object.h \
+ bool_object.h number_object.h date_object.h array_object.h \
+ math_object.h object_object.h regexp_object.h error_object.h \
+ debugger.h array_instance.h context.h dtoa.h regexp.h
+
+if include_VERSION_SCRIPT
+VSCRIPT = -Wl,--version-script=$(srcdir)/libkjs.map
+endif
+
+libkjs_la_LDFLAGS = -version-info 3:0:2 -no-undefined $(VSCRIPT) \
+ $(USER_LDFLAGS) $(all_libraries)
+libkjs_la_LIBADD = -lm $(LIBPCRE)
+
+EXTRA_DIST = grammar.y
+
+parser: $(srcdir)/grammar.y
+ cd $(srcdir); \
+ $(YACC) -d -p kjsyy grammar.y && mv grammar.tab.c grammar.cpp; \
+ if test -f grammar.tab.h; then \
+ if cmp -s grammar.tab.h grammar.h; then rm -f grammar.tab.h; \
+ else mv grammar.tab.h grammar.h; fi \
+ else :; fi
+
+LUT_FILES = math_object.lut.h lexer.lut.h array_object.lut.h date_object.lut.h string_object.lut.h number_object.lut.h
+
+CREATE_HASH_TABLE = $(srcdir)/create_hash_table
+
+lexer.lut.h: $(srcdir)/keywords.table $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/keywords.table -i > $@
+lexer.lo: lexer.lut.h
+
+# Can't use %.lut.h: %.cpp, it's not portable.
+
+array_object.lut.h : $(srcdir)/array_object.cpp $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/array_object.cpp -i > $@
+array_object.lo: array_object.lut.h
+math_object.lut.h : $(srcdir)/math_object.cpp $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/math_object.cpp -i > $@
+math_object.lo: math_object.lut.h
+date_object.lut.h : $(srcdir)/date_object.cpp $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/date_object.cpp -i > $@
+date_object.lo: date_object.lut.h
+number_object.lut.h : $(srcdir)/number_object.cpp $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/number_object.cpp -i > $@
+number_object.lo: number_object.lut.h
+string_object.lut.h : $(srcdir)/string_object.cpp $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/string_object.cpp -i > $@
+string_object.lo: string_object.lut.h
+
+CLEANFILES = $(LUT_FILES)
+
+## test program (in one program for easier profiling/memory debugging)
+EXTRA_PROGRAMS = testkjs_static
+testkjs_static_SOURCES = testkjs.cpp
+testkjs_static_LDADD = $(LIBPCRE) libkjs.la
+testkjs_static_LDFLAGS = -static
+
+## test program (linked to libkjs)
+check_PROGRAMS = testkjs
+testkjs_SOURCES = testkjs.cpp
+testkjs_LDADD = libkjs.la
+
+DOXYGEN_REFERENCES = kdecore
+include ../admin/Doxyfile.am
+
+.PHONY: parser
+
diff --git a/kjs/README b/kjs/README
new file mode 100644
index 000000000..7e01b0b41
--- /dev/null
+++ b/kjs/README
@@ -0,0 +1,27 @@
+This library provides an ECMAScript compatible interpreter. The ECMA standard
+is based on well known scripting languages such as Netscape's JavaScript and
+Microsoft's JScript.
+
+I'm currently pursuing to be compliant with Edition 3 of ECMA-262. Postscript
+and pdf versions of the standard are available at:
+
+http://www.ecma-international.org/publications/files/ecma-st/Ecma-262.pdf
+for PDF and
+http://www.ecma-international.org/publications/standards/Ecma-262.htm
+for the standard page.
+
+About 95% of the required features should be covered by now. Note that this
+number covers the core language elements only. Features like the famous
+roll-over buttons on the www are NOT part of the standard. Those extensions
+are added via a module loaded dynamically by the KHTML Widget.
+
+I'll provide some examples of how to extend this library for various needs at
+a later point in time. Feel free to contact me via mail if you have any
+questions on how to provide scripting capabilities for your application.
+
+A debugger is being worked on.
+
+Bug reports, patches or feedback of any kind is very welcome.
+
+Harri Porten <porten@kde.org>
+
diff --git a/kjs/THANKS b/kjs/THANKS
new file mode 100644
index 000000000..036e5e75c
--- /dev/null
+++ b/kjs/THANKS
@@ -0,0 +1,7 @@
+I would like to thank the following people for their help:
+
+Richard Moore <rich@kde.org> - for filling the Math object with some life
+Daegeun Lee <realking@mizi.com> - for pointing out some bugs and providing
+ much code for the String and Date object.
+Marco Pinelli <pinmc@libero.it> - for his patches
+Christian Kirsch <ck@held.mind.de> - for his contribution to the Date object
diff --git a/kjs/array_instance.h b/kjs/array_instance.h
new file mode 100644
index 000000000..2c2db0e0c
--- /dev/null
+++ b/kjs/array_instance.h
@@ -0,0 +1,71 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 {
+
+ class ArrayInstanceImp : public ObjectImp {
+ public:
+ ArrayInstanceImp(ObjectImp *proto, unsigned initialLength);
+ ArrayInstanceImp(ObjectImp *proto, const List &initialValues);
+ ~ArrayInstanceImp();
+
+ virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+ virtual Value getPropertyByIndex(ExecState *exec, unsigned propertyName) const;
+ virtual void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr = None);
+ virtual void putPropertyByIndex(ExecState *exec, unsigned propertyName, const Value &value, int attr = None);
+ virtual bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
+ virtual bool hasPropertyByIndex(ExecState *exec, unsigned propertyName) const;
+ virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName);
+ virtual bool deletePropertyByIndex(ExecState *exec, unsigned propertyName);
+ virtual ReferenceList propList(ExecState *exec, bool recursive);
+
+ virtual void mark();
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+
+ unsigned getLength() const { return length; }
+
+ void sort(ExecState *exec);
+ void sort(ExecState *exec, Object &compareFunction);
+
+ private:
+ void setLength(unsigned newLength, ExecState *exec);
+
+ unsigned pushUndefinedObjectsToEnd(ExecState *exec);
+
+ void resizeStorage(unsigned);
+
+ unsigned length;
+ unsigned storageLength;
+ unsigned capacity;
+ ValueImp **storage;
+ };
+
+} // namespace KJS
+
+#endif
diff --git a/kjs/array_object.cpp b/kjs/array_object.cpp
new file mode 100644
index 000000000..a23e08dae
--- /dev/null
+++ b/kjs/array_object.cpp
@@ -0,0 +1,869 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "array_object.h"
+#include "internal.h"
+#include "error_object.h"
+
+#include "array_object.lut.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#define MAX_INDEX 4294967294U // 2^32-2
+
+using namespace KJS;
+
+// ------------------------------ ArrayInstanceImp -----------------------------
+
+const unsigned sparseArrayCutoff = 10000;
+
+const ClassInfo ArrayInstanceImp::info = {"Array", 0, 0, 0};
+
+ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto, unsigned initialLength)
+ : ObjectImp(proto)
+ , length(initialLength)
+ , storageLength(initialLength < sparseArrayCutoff ? initialLength : 0)
+ , capacity(storageLength)
+ , storage(capacity ? (ValueImp **)calloc(capacity, sizeof(ValueImp *)) : 0)
+{
+}
+
+ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto, const List &list)
+ : ObjectImp(proto)
+ , length(list.size())
+ , storageLength(length)
+ , capacity(storageLength)
+ , storage(capacity ? (ValueImp **)malloc(sizeof(ValueImp *) * capacity) : 0)
+{
+ ListIterator it = list.begin();
+ unsigned l = length;
+ for (unsigned i = 0; i < l; ++i) {
+ storage[i] = (it++).imp();
+ }
+}
+
+ArrayInstanceImp::~ArrayInstanceImp()
+{
+ free(storage);
+}
+
+Value ArrayInstanceImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == lengthPropertyName)
+ return Number(length);
+
+ bool ok;
+ unsigned index = propertyName.toArrayIndex(&ok);
+ if (ok) {
+ if (index >= length)
+ return Undefined();
+ if (index < storageLength) {
+ ValueImp *v = storage[index];
+ return v ? Value(v) : Undefined();
+ }
+ }
+
+ return ObjectImp::get(exec, propertyName);
+}
+
+Value ArrayInstanceImp::getPropertyByIndex(ExecState *exec,
+ unsigned index) const
+{
+ if (index > MAX_INDEX)
+ return ObjectImp::get(exec, Identifier::from(index));
+ if (index >= length)
+ return Undefined();
+ if (index < storageLength) {
+ ValueImp *v = storage[index];
+ return v ? Value(v) : Undefined();
+ }
+
+ return ObjectImp::get(exec, Identifier::from(index));
+}
+
+// Special implementation of [[Put]] - see ECMA 15.4.5.1
+void ArrayInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
+{
+ if (propertyName == lengthPropertyName) {
+ unsigned int newLen = value.toUInt32(exec);
+ if (value.toNumber(exec) != double(newLen)) {
+ Object err = Error::create(exec, RangeError, "Invalid array length.");
+ exec->setException(err);
+ return;
+ }
+ setLength(newLen, exec);
+ return;
+ }
+
+ bool ok;
+ unsigned index = propertyName.toArrayIndex(&ok);
+ if (ok) {
+ putPropertyByIndex(exec, index, value, attr);
+ return;
+ }
+
+ ObjectImp::put(exec, propertyName, value, attr);
+}
+
+void ArrayInstanceImp::putPropertyByIndex(ExecState *exec, unsigned index,
+ const Value &value, int attr)
+{
+ if (index < sparseArrayCutoff && index >= storageLength) {
+ resizeStorage(index + 1);
+ }
+
+ if (index >= length && index <= MAX_INDEX) {
+ length = index + 1;
+ }
+
+ if (index < storageLength) {
+ storage[index] = value.imp();
+ return;
+ }
+
+ assert(index >= sparseArrayCutoff);
+ ObjectImp::put(exec, Identifier::from(index), value, attr);
+}
+
+bool ArrayInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == lengthPropertyName)
+ return true;
+
+ bool ok;
+ unsigned index = propertyName.toArrayIndex(&ok);
+ if (ok) {
+ if (index >= length)
+ return false;
+ if (index < storageLength) {
+ ValueImp *v = storage[index];
+ return v && v != UndefinedImp::staticUndefined;
+ }
+ }
+
+ return ObjectImp::hasProperty(exec, propertyName);
+}
+
+bool ArrayInstanceImp::hasPropertyByIndex(ExecState *exec, unsigned index) const
+{
+ if (index > MAX_INDEX)
+ return ObjectImp::hasProperty(exec, Identifier::from(index));
+ if (index >= length)
+ return false;
+ if (index < storageLength) {
+ ValueImp *v = storage[index];
+ return v && v != UndefinedImp::staticUndefined;
+ }
+
+ return ObjectImp::hasProperty(exec, Identifier::from(index));
+}
+
+bool ArrayInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
+{
+ if (propertyName == lengthPropertyName)
+ return false;
+
+ bool ok;
+ unsigned index = propertyName.toArrayIndex(&ok);
+ if (ok) {
+ if (index >= length)
+ return true;
+ if (index < storageLength) {
+ storage[index] = 0;
+ return true;
+ }
+ }
+
+ return ObjectImp::deleteProperty(exec, propertyName);
+}
+
+bool ArrayInstanceImp::deletePropertyByIndex(ExecState *exec, unsigned index)
+{
+ if (index > MAX_INDEX)
+ return ObjectImp::deleteProperty(exec, Identifier::from(index));
+ if (index >= length)
+ return true;
+ if (index < storageLength) {
+ storage[index] = 0;
+ return true;
+ }
+
+ return ObjectImp::deleteProperty(exec, Identifier::from(index));
+}
+
+ReferenceList ArrayInstanceImp::propList(ExecState *exec, bool recursive)
+{
+ ReferenceList properties = ObjectImp::propList(exec,recursive);
+
+ // avoid fetching this every time through the loop
+ ValueImp *undefined = UndefinedImp::staticUndefined;
+
+ for (unsigned i = 0; i < storageLength; ++i) {
+ ValueImp *imp = storage[i];
+ if (imp && imp != undefined && !ObjectImp::hasProperty(exec,Identifier::from(i))) {
+ properties.append(Reference(this, i));
+ }
+ }
+ return properties;
+}
+
+void ArrayInstanceImp::resizeStorage(unsigned newLength)
+{
+ if (newLength < storageLength) {
+ memset(storage + newLength, 0, sizeof(ValueImp *) * (storageLength - newLength));
+ }
+ if (newLength > capacity) {
+ unsigned newCapacity;
+ if (newLength > sparseArrayCutoff) {
+ newCapacity = newLength;
+ } else {
+ newCapacity = (newLength * 3 + 1) / 2;
+ if (newCapacity > sparseArrayCutoff) {
+ newCapacity = sparseArrayCutoff;
+ }
+ }
+ storage = (ValueImp **)realloc(storage, newCapacity * sizeof (ValueImp *));
+ memset(storage + capacity, 0, sizeof(ValueImp *) * (newCapacity - capacity));
+ capacity = newCapacity;
+ }
+ storageLength = newLength;
+}
+
+void ArrayInstanceImp::setLength(unsigned newLength, ExecState *exec)
+{
+ if (newLength <= storageLength) {
+ resizeStorage(newLength);
+ }
+
+ if (newLength < length) {
+ ReferenceList sparseProperties;
+
+ _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this));
+
+ ReferenceListIterator it = sparseProperties.begin();
+ while (it != sparseProperties.end()) {
+ Reference ref = it++;
+ bool ok;
+ unsigned index = ref.getPropertyName(exec).toArrayIndex(&ok);
+ if (ok && index > newLength) {
+ ref.deleteValue(exec);
+ }
+ }
+ }
+
+ length = newLength;
+}
+
+void ArrayInstanceImp::mark()
+{
+ ObjectImp::mark();
+ unsigned l = storageLength;
+ for (unsigned i = 0; i < l; ++i) {
+ ValueImp *imp = storage[i];
+ if (imp && !imp->marked())
+ imp->mark();
+ }
+}
+
+static ExecState *execForCompareByStringForQSort;
+
+static int compareByStringForQSort(const void *a, const void *b)
+{
+ ExecState *exec = execForCompareByStringForQSort;
+ ValueImp *va = *(ValueImp **)a;
+ ValueImp *vb = *(ValueImp **)b;
+ if (va->dispatchType() == UndefinedType) {
+ return vb->dispatchType() == UndefinedType ? 0 : 1;
+ }
+ if (vb->dispatchType() == UndefinedType) {
+ return -1;
+ }
+ return compare(va->dispatchToString(exec), vb->dispatchToString(exec));
+}
+
+void ArrayInstanceImp::sort(ExecState *exec)
+{
+ int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
+
+ execForCompareByStringForQSort = exec;
+ qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareByStringForQSort);
+ execForCompareByStringForQSort = 0;
+}
+
+namespace KJS {
+
+struct CompareWithCompareFunctionArguments {
+ CompareWithCompareFunctionArguments(ExecState *e, ObjectImp *cf)
+ : exec(e)
+ , compareFunction(cf)
+ , globalObject(e->dynamicInterpreter()->globalObject())
+ {
+ arguments.append(Undefined());
+ arguments.append(Undefined());
+ }
+
+ ExecState *exec;
+ ObjectImp *compareFunction;
+ List arguments;
+ Object globalObject;
+};
+
+}
+
+static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
+
+static int compareWithCompareFunctionForQSort(const void *a, const void *b)
+{
+ CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
+
+ ValueImp *va = *(ValueImp **)a;
+ ValueImp *vb = *(ValueImp **)b;
+ if (va->dispatchType() == UndefinedType) {
+ return vb->dispatchType() == UndefinedType ? 0 : 1;
+ }
+ if (vb->dispatchType() == UndefinedType) {
+ return -1;
+ }
+
+ 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 ArrayInstanceImp::sort(ExecState *exec, Object &compareFunction)
+{
+ int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
+
+ CompareWithCompareFunctionArguments args(exec, compareFunction.imp());
+ compareWithCompareFunctionArguments = &args;
+ qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareWithCompareFunctionForQSort);
+ compareWithCompareFunctionArguments = 0;
+}
+
+unsigned ArrayInstanceImp::pushUndefinedObjectsToEnd(ExecState *exec)
+{
+ ValueImp *undefined = UndefinedImp::staticUndefined;
+
+ unsigned o = 0;
+
+ for (unsigned i = 0; i != storageLength; ++i) {
+ ValueImp *v = storage[i];
+ if (v && v != undefined) {
+ if (o != i)
+ storage[o] = v;
+ o++;
+ }
+ }
+
+ ReferenceList sparseProperties;
+ _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this));
+ unsigned newLength = o + sparseProperties.length();
+
+ if (newLength > storageLength) {
+ resizeStorage(newLength);
+ }
+
+ ReferenceListIterator it = sparseProperties.begin();
+ while (it != sparseProperties.end()) {
+ Reference ref = it++;
+ storage[o] = ref.getValue(exec).imp();
+ ObjectImp::deleteProperty(exec, ref.getPropertyName(exec));
+ o++;
+ }
+
+ if (newLength != storageLength)
+ memset(storage + o, 0, sizeof(ValueImp *) * (storageLength - o));
+
+ return o;
+}
+
+// ------------------------------ ArrayPrototypeImp ----------------------------
+
+const ClassInfo ArrayPrototypeImp::info = {"Array", &ArrayInstanceImp::info, &arrayTable, 0};
+
+/* Source for array_object.lut.h
+@begin arrayTable 17
+ toString ArrayProtoFuncImp::ToString DontEnum|Function 0
+ toLocaleString ArrayProtoFuncImp::ToLocaleString DontEnum|Function 0
+ concat ArrayProtoFuncImp::Concat DontEnum|Function 1
+ join ArrayProtoFuncImp::Join DontEnum|Function 1
+ pop ArrayProtoFuncImp::Pop DontEnum|Function 0
+ push ArrayProtoFuncImp::Push DontEnum|Function 1
+ reverse ArrayProtoFuncImp::Reverse DontEnum|Function 0
+ shift ArrayProtoFuncImp::Shift DontEnum|Function 0
+ slice ArrayProtoFuncImp::Slice DontEnum|Function 2
+ sort ArrayProtoFuncImp::Sort DontEnum|Function 1
+ splice ArrayProtoFuncImp::Splice DontEnum|Function 2
+ unshift ArrayProtoFuncImp::UnShift DontEnum|Function 1
+@end
+*/
+
+// ECMA 15.4.4
+ArrayPrototypeImp::ArrayPrototypeImp(ExecState */*exec*/,
+ ObjectPrototypeImp *objProto)
+ : ArrayInstanceImp(objProto, 0)
+{
+ Value protect(this);
+ setInternalValue(Null());
+}
+
+Value ArrayPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ //fprintf( stderr, "ArrayPrototypeImp::get(%s)\n", propertyName.ascii() );
+ return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this );
+}
+
+// ------------------------------ ArrayProtoFuncImp ----------------------------
+
+ArrayProtoFuncImp::ArrayProtoFuncImp(ExecState *exec, int i, int len)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
+ ), id(i)
+{
+ Value protect(this);
+ put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum);
+}
+
+bool ArrayProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.4.4
+Value ArrayProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ unsigned int length = thisObj.get(exec,lengthPropertyName).toUInt32(exec);
+
+ Value result;
+ switch (id) {
+ case ToLocaleString:
+ case ToString:
+
+ if (!thisObj.inherits(&ArrayInstanceImp::info)) {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+ // fall through
+ case Join: {
+ UString separator = ",";
+ UString str = "";
+
+ if (id == Join && args.size() > 0 && !args[0].isA(UndefinedType))
+ separator = args[0].toString(exec);
+ for (unsigned int k = 0; k < length; k++) {
+ if (k >= 1)
+ str += separator;
+
+ Value element = thisObj.get(exec, k);
+ if (element.type() == UndefinedType || element.type() == NullType)
+ continue;
+
+ bool fallback = false;
+ if (id == ToLocaleString) {
+ Object o = element.toObject(exec);
+ Object conversionFunction =
+ Object::dynamicCast(o.get(exec, toLocaleStringPropertyName));
+ if (conversionFunction.isValid() &&
+ conversionFunction.implementsCall()) {
+ str += conversionFunction.call(exec, o, List()).toString(exec);
+ } else {
+ // try toString() fallback
+ fallback = true;
+ }
+ }
+ if (id == ToString || id == Join || fallback) {
+ if (element.type() == ObjectType) {
+ Object o = Object::dynamicCast(element);
+ Object conversionFunction =
+ Object::dynamicCast(o.get(exec, toStringPropertyName));
+ if (conversionFunction.isValid() &&
+ conversionFunction.implementsCall()) {
+ str += conversionFunction.call(exec, o, List()).toString(exec);
+ } else {
+ UString msg = "Can't convert " + o.className() +
+ " object to string";
+ Object error = Error::create(exec, RangeError,
+ msg.cstring().c_str());
+ exec->setException(error);
+ return error;
+ }
+ } else {
+ str += element.toString(exec);
+ }
+ }
+ if ( exec->hadException() )
+ break;
+ }
+ result = String(str);
+ break;
+ }
+ case Concat: {
+ Object arr = Object::dynamicCast(exec->lexicalInterpreter()->builtinArray().construct(exec,List::empty()));
+ int n = 0;
+ Value curArg = thisObj;
+ Object curObj = Object::dynamicCast(thisObj);
+ ListIterator it = args.begin();
+ for (;;) {
+ if (curArg.type() == ObjectType &&
+ curObj.inherits(&ArrayInstanceImp::info)) {
+ unsigned int 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.
+ length = curObj.get(exec,lengthPropertyName).toUInt32(exec);
+ while (k < length) {
+ if (curObj.hasProperty(exec,k))
+ arr.put(exec, n, curObj.get(exec, k));
+ n++;
+ k++;
+ }
+ } else {
+ arr.put(exec, n, curArg);
+ n++;
+ }
+ if (it == args.end())
+ break;
+ curArg = *it;
+ curObj = Object::dynamicCast(it++); // may be 0
+ }
+ arr.put(exec,lengthPropertyName, Number(n), DontEnum | DontDelete);
+
+ result = arr;
+ break;
+ }
+ case Pop:{
+ if (length == 0) {
+ thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete);
+ result = Undefined();
+ } else {
+ result = thisObj.get(exec, length - 1);
+ thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete);
+ }
+ break;
+ }
+ case Push: {
+ for (int n = 0; n < args.size(); n++)
+ thisObj.put(exec, length + n, args[n]);
+ length += args.size();
+ thisObj.put(exec,lengthPropertyName, Number(length), DontEnum | DontDelete);
+ result = Number(length);
+ break;
+ }
+ case Reverse: {
+
+ unsigned int middle = length / 2;
+
+ for (unsigned int k = 0; k < middle; k++) {
+ unsigned lk1 = length - k - 1;
+ Value obj = thisObj.get(exec,k);
+ Value obj2 = thisObj.get(exec,lk1);
+ if (thisObj.hasProperty(exec,lk1)) {
+ if (thisObj.hasProperty(exec,k)) {
+ thisObj.put(exec, k, obj2);
+ thisObj.put(exec, lk1, obj);
+ } else {
+ thisObj.put(exec, k, obj2);
+ thisObj.deleteProperty(exec, lk1);
+ }
+ } else {
+ if (thisObj.hasProperty(exec, k)) {
+ thisObj.deleteProperty(exec, k);
+ thisObj.put(exec, lk1, obj);
+ } else {
+ // why delete something that's not there ? Strange.
+ thisObj.deleteProperty(exec, k);
+ thisObj.deleteProperty(exec, lk1);
+ }
+ }
+ }
+ result = thisObj;
+ break;
+ }
+ case Shift: {
+ if (length == 0) {
+ thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete);
+ result = Undefined();
+ } else {
+ result = thisObj.get(exec, 0);
+ for(unsigned int k = 1; k < length; k++) {
+ if (thisObj.hasProperty(exec, k)) {
+ Value obj = thisObj.get(exec, k);
+ thisObj.put(exec, k-1, obj);
+ } else
+ thisObj.deleteProperty(exec, k-1);
+ }
+ thisObj.deleteProperty(exec, length - 1);
+ thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete);
+ }
+ break;
+ }
+ case Slice: {
+ // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
+
+ // We return a new array
+ Object resObj = Object::dynamicCast(exec->lexicalInterpreter()->builtinArray().construct(exec,List::empty()));
+ result = resObj;
+ int begin = 0;
+ if (args[0].type() != UndefinedType) {
+ begin = args[0].toInteger(exec);
+ if ( begin < 0 )
+ begin = maxInt( begin + length, 0 );
+ else
+ begin = minInt( begin, length );
+ }
+ int end = length;
+ if (args[1].type() != UndefinedType)
+ {
+ end = args[1].toInteger(exec);
+ if ( end < 0 )
+ end = maxInt( end + length, 0 );
+ else
+ end = minInt( end, length );
+ }
+
+ //printf( "Slicing from %d to %d \n", begin, end );
+ int n = 0;
+ for(int k = begin; k < end; k++, n++) {
+ if (thisObj.hasProperty(exec, k)) {
+ Value obj = thisObj.get(exec, k);
+ resObj.put(exec, n, obj);
+ }
+ }
+ resObj.put(exec, lengthPropertyName, Number(n), DontEnum | DontDelete);
+ break;
+ }
+ case Sort:{
+#if 0
+ printf("KJS Array::Sort length=%d\n", length);
+ for ( unsigned int i = 0 ; i<length ; ++i )
+ printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() );
+#endif
+ Object sortFunction;
+ bool useSortFunction = (args[0].type() != UndefinedType);
+ if (useSortFunction)
+ {
+ sortFunction = args[0].toObject(exec);
+ if (!sortFunction.implementsCall())
+ useSortFunction = false;
+ }
+
+ if (thisObj.imp()->classInfo() == &ArrayInstanceImp::info) {
+ if (useSortFunction)
+ ((ArrayInstanceImp *)thisObj.imp())->sort(exec, sortFunction);
+ else
+ ((ArrayInstanceImp *)thisObj.imp())->sort(exec);
+ result = thisObj;
+ break;
+ }
+
+ if (length == 0) {
+ thisObj.put(exec, lengthPropertyName, Number(0), DontEnum | DontDelete);
+ result = thisObj;
+ break;
+ }
+
+ // "Min" sort. Not the fastest, but definitely less code than heapsort
+ // or quicksort, and much less swapping than bubblesort/insertionsort.
+ for ( unsigned int i = 0 ; i<length-1 ; ++i )
+ {
+ Value iObj = thisObj.get(exec,i);
+ unsigned int themin = i;
+ Value minObj = iObj;
+ for ( unsigned int j = i+1 ; j<length ; ++j )
+ {
+ Value jObj = thisObj.get(exec,j);
+ double cmp;
+ if (jObj.type() == UndefinedType) {
+ cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
+ } else if (minObj.type() == UndefinedType) {
+ cmp = -1;
+ } else if (useSortFunction) {
+ List l;
+ l.append(jObj);
+ l.append(minObj);
+ cmp = sortFunction.call(exec, exec->dynamicInterpreter()->globalObject(), l).toNumber(exec);
+ } else {
+ cmp = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1;
+ }
+ if ( cmp < 0 )
+ {
+ themin = j;
+ minObj = jObj;
+ }
+ }
+ // Swap themin and i
+ if ( themin > i )
+ {
+ //printf("KJS Array::Sort: swapping %d and %d\n", i, themin );
+ thisObj.put( exec, i, minObj );
+ thisObj.put( exec, themin, iObj );
+ }
+ }
+#if 0
+ printf("KJS Array::Sort -- Resulting array:\n");
+ for ( unsigned int i = 0 ; i<length ; ++i )
+ printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() );
+#endif
+ result = thisObj;
+ break;
+ }
+ case Splice: {
+ // 15.4.4.12 - oh boy this is huge
+ Object resObj = Object::dynamicCast(exec->lexicalInterpreter()->builtinArray().construct(exec,List::empty()));
+ result = resObj;
+ int begin = args[0].toUInt32(exec);
+ if ( begin < 0 )
+ begin = maxInt( begin + length, 0 );
+ else
+ begin = minInt( begin, length );
+ unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin );
+
+ //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount );
+ for(unsigned int k = 0; k < deleteCount; k++) {
+ if (thisObj.hasProperty(exec,k+begin)) {
+ Value obj = thisObj.get(exec, k+begin);
+ resObj.put(exec, k, obj);
+ }
+ }
+ resObj.put(exec, lengthPropertyName, Number(deleteCount), DontEnum | DontDelete);
+
+ unsigned int additionalArgs = maxInt( args.size() - 2, 0 );
+ if ( additionalArgs != deleteCount )
+ {
+ if ( additionalArgs < deleteCount )
+ {
+ for ( unsigned int k = begin; k < length - deleteCount; ++k )
+ {
+ if (thisObj.hasProperty(exec,k+deleteCount)) {
+ Value obj = thisObj.get(exec, k+deleteCount);
+ thisObj.put(exec, k+additionalArgs, obj);
+ }
+ else
+ thisObj.deleteProperty(exec, k+additionalArgs);
+ }
+ for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
+ thisObj.deleteProperty(exec, k-1);
+ }
+ else
+ {
+ for ( unsigned int k = length - deleteCount; (int)k > begin; --k )
+ {
+ if (thisObj.hasProperty(exec,k+deleteCount-1)) {
+ Value obj = thisObj.get(exec, k+deleteCount-1);
+ thisObj.put(exec, k+additionalArgs-1, obj);
+ }
+ else
+ thisObj.deleteProperty(exec, k+additionalArgs-1);
+ }
+ }
+ }
+ for ( unsigned int k = 0; k < additionalArgs; ++k )
+ {
+ thisObj.put(exec, k+begin, args[k+2]);
+ }
+ thisObj.put(exec, lengthPropertyName, Number(length - deleteCount + additionalArgs), DontEnum | DontDelete);
+ break;
+ }
+ case UnShift: { // 15.4.4.13
+ unsigned int nrArgs = args.size();
+ for ( unsigned int k = length; k > 0; --k )
+ {
+ if (thisObj.hasProperty(exec,k-1)) {
+ Value obj = thisObj.get(exec, k-1);
+ thisObj.put(exec, k+nrArgs-1, obj);
+ } else {
+ thisObj.deleteProperty(exec, k+nrArgs-1);
+ }
+ }
+ for ( unsigned int k = 0; k < nrArgs; ++k )
+ thisObj.put(exec, k, args[k]);
+ result = Number(length + nrArgs);
+ thisObj.put(exec, lengthPropertyName, result, DontEnum | DontDelete);
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ return result;
+}
+
+// ------------------------------ ArrayObjectImp -------------------------------
+
+ArrayObjectImp::ArrayObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ ArrayPrototypeImp *arrayProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ // ECMA 15.4.3.1 Array.prototype
+ put(exec,prototypePropertyName, Object(arrayProto), DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ put(exec,lengthPropertyName, Number(1), ReadOnly|DontDelete|DontEnum);
+}
+
+bool ArrayObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.4.2
+Object ArrayObjectImp::construct(ExecState *exec, const List &args)
+{
+ // a single numeric argument denotes the array size (!)
+ if (args.size() == 1 && args[0].type() == NumberType) {
+ unsigned int n = args[0].toUInt32(exec);
+ if (n != args[0].toNumber(exec)) {
+ Object error = Error::create(exec, RangeError, "Invalid array length.");
+ exec->setException(error);
+ return error;
+ }
+ return Object(new ArrayInstanceImp(exec->lexicalInterpreter()->builtinArrayPrototype().imp(), n));
+ }
+
+ // otherwise the array is constructed with the arguments in it
+ return Object(new ArrayInstanceImp(exec->lexicalInterpreter()->builtinArrayPrototype().imp(), args));
+}
+
+bool ArrayObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.6.1
+Value ArrayObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ // equivalent to 'new Array(....)'
+ return construct(exec,args);
+}
diff --git a/kjs/array_object.h b/kjs/array_object.h
new file mode 100644
index 000000000..f25340177
--- /dev/null
+++ b/kjs/array_object.h
@@ -0,0 +1,67 @@
+// -*- 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 _ARRAY_OBJECT_H_
+#define _ARRAY_OBJECT_H_
+
+#include "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class ArrayPrototypeImp : public ArrayInstanceImp {
+ public:
+ ArrayPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto);
+ Value get(ExecState *exec, const Identifier &p) const;
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ };
+
+ class ArrayProtoFuncImp : public InternalFunctionImp {
+ public:
+ ArrayProtoFuncImp(ExecState *exec, int i, int len);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { ToString, ToLocaleString, Concat, Join, Pop, Push,
+ Reverse, Shift, Slice, Sort, Splice, UnShift };
+ private:
+ int id;
+ };
+
+ class ArrayObjectImp : public InternalFunctionImp {
+ public:
+ ArrayObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ ArrayPrototypeImp *arrayProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/bool_object.cpp b/kjs/bool_object.cpp
new file mode 100644
index 000000000..5462a46c2
--- /dev/null
+++ b/kjs/bool_object.cpp
@@ -0,0 +1,146 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "bool_object.h"
+#include "error_object.h"
+#include "lookup.h"
+
+#include <assert.h>
+
+using namespace KJS;
+
+// ------------------------------ BooleanInstanceImp ---------------------------
+
+const ClassInfo BooleanInstanceImp::info = {"Boolean", 0, 0, 0};
+
+BooleanInstanceImp::BooleanInstanceImp(ObjectImp *proto)
+ : ObjectImp(proto)
+{
+}
+
+// ------------------------------ BooleanPrototypeImp --------------------------
+
+// ECMA 15.6.4
+
+BooleanPrototypeImp::BooleanPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto)
+ : BooleanInstanceImp(objectProto)
+{
+ Value protect(this);
+ // The constructor will be added later by InterpreterImp::InterpreterImp()
+
+ putDirect(toStringPropertyName, new BooleanProtoFuncImp(exec,funcProto,BooleanProtoFuncImp::ToString,0,toStringPropertyName), DontEnum);
+ putDirect(valueOfPropertyName, new BooleanProtoFuncImp(exec,funcProto,BooleanProtoFuncImp::ValueOf,0,valueOfPropertyName), DontEnum);
+ setInternalValue(Boolean(false));
+}
+
+
+// ------------------------------ BooleanProtoFuncImp --------------------------
+
+BooleanProtoFuncImp::BooleanProtoFuncImp(ExecState * /*exec*/,
+ FunctionPrototypeImp *funcProto, int i, int len, const Identifier &_ident)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+ ident = _ident;
+}
+
+
+bool BooleanProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+
+// ECMA 15.6.4.2 + 15.6.4.3
+Value BooleanProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &/*args*/)
+{
+ // no generic function. "this" has to be a Boolean object
+ KJS_CHECK_THIS( BooleanInstanceImp, thisObj );
+
+ // execute "toString()" or "valueOf()", respectively
+
+ Value v = thisObj.internalValue();
+ assert(v.isValid());
+
+ if (id == ToString)
+ return String(v.toString(exec));
+ return Boolean(v.toBoolean(exec)); /* TODO: optimize for bool case */
+}
+
+// ------------------------------ BooleanObjectImp -----------------------------
+
+
+BooleanObjectImp::BooleanObjectImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
+ BooleanPrototypeImp *booleanProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ putDirect(prototypePropertyName, booleanProto, DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
+}
+
+
+bool BooleanObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.6.2
+Object BooleanObjectImp::construct(ExecState *exec, const List &args)
+{
+ Object obj(new BooleanInstanceImp(exec->lexicalInterpreter()->builtinBooleanPrototype().imp()));
+
+ Boolean b;
+ if (args.size() > 0)
+ b = args.begin()->dispatchToBoolean(exec);
+ else
+ b = Boolean(false);
+
+ obj.setInternalValue(b);
+
+ return obj;
+}
+
+bool BooleanObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.6.1
+Value BooleanObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ if (args.isEmpty())
+ return Boolean(false);
+ else
+ return Boolean(args[0].toBoolean(exec)); /* TODO: optimize for bool case */
+}
+
diff --git a/kjs/bool_object.h b/kjs/bool_object.h
new file mode 100644
index 000000000..0fd87336a
--- /dev/null
+++ b/kjs/bool_object.h
@@ -0,0 +1,90 @@
+// -*- 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 _BOOL_OBJECT_H_
+#define _BOOL_OBJECT_H_
+
+#include "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class BooleanInstanceImp : public ObjectImp {
+ public:
+ BooleanInstanceImp(ObjectImp *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 BooleanPrototypeImp : public BooleanInstanceImp {
+ public:
+ BooleanPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto);
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Boolean.prototype object
+ */
+ class BooleanProtoFuncImp : public InternalFunctionImp {
+ public:
+ BooleanProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { ToString, ValueOf };
+ private:
+ int id;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "Boolean" property
+ */
+ class BooleanObjectImp : public InternalFunctionImp {
+ friend class BooleanProtoFuncImp;
+ public:
+ BooleanObjectImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ BooleanPrototypeImp *booleanProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/collector.cpp b/kjs/collector.cpp
new file mode 100644
index 000000000..62d594329
--- /dev/null
+++ b/kjs/collector.cpp
@@ -0,0 +1,312 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 "collector.h"
+
+#include "value.h"
+#include "internal.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+namespace KJS {
+
+// tunable parameters
+const int MINIMUM_CELL_SIZE = 56;
+const int BLOCK_SIZE = (8 * 4096);
+const int SPARE_EMPTY_BLOCKS = 2;
+const int MIN_ARRAY_SIZE = 14;
+const int GROWTH_FACTOR = 2;
+const int LOW_WATER_FACTOR = 4;
+const int ALLOCATIONS_PER_COLLECTION = 1000;
+
+// derived constants
+const int CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0);
+const int CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double);
+const int CELLS_PER_BLOCK = ((BLOCK_SIZE * 8 - sizeof(int) * 8 - sizeof(void *) * 8) / (CELL_SIZE * 8));
+
+
+
+struct CollectorCell {
+ double memory[CELL_ARRAY_LENGTH];
+};
+
+
+struct CollectorBlock {
+ CollectorCell cells[CELLS_PER_BLOCK];
+ int usedCells;
+ CollectorCell *freeList;
+};
+
+struct CollectorHeap {
+ CollectorBlock **blocks;
+ int numBlocks;
+ int usedBlocks;
+ int firstBlockWithPossibleSpace;
+
+ CollectorCell **oversizeCells;
+ int numOversizeCells;
+ int usedOversizeCells;
+
+ int numLiveObjects;
+ int numAllocationsSinceLastCollect;
+};
+
+static CollectorHeap heap = {NULL, 0, 0, 0, NULL, 0, 0, 0, 0};
+
+bool Collector::memoryFull = false;
+
+void* Collector::allocate(size_t s)
+{
+ if (s == 0)
+ return 0L;
+
+ // collect if needed
+ if (++heap.numAllocationsSinceLastCollect >= ALLOCATIONS_PER_COLLECTION) {
+ collect();
+ }
+
+ if (s > (unsigned)CELL_SIZE) {
+ // oversize allocator
+ if (heap.usedOversizeCells == heap.numOversizeCells) {
+ heap.numOversizeCells = MAX(MIN_ARRAY_SIZE, heap.numOversizeCells * GROWTH_FACTOR);
+ heap.oversizeCells = (CollectorCell **)realloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
+ }
+
+ void *newCell = malloc(s);
+ heap.oversizeCells[heap.usedOversizeCells] = (CollectorCell *)newCell;
+ heap.usedOversizeCells++;
+ heap.numLiveObjects++;
+
+ ((ValueImp *)(newCell))->_flags = 0;
+ return newCell;
+ }
+
+ // slab allocator
+
+ CollectorBlock *targetBlock = NULL;
+
+ int i;
+ for (i = heap.firstBlockWithPossibleSpace; i < heap.usedBlocks; i++) {
+ if (heap.blocks[i]->usedCells < CELLS_PER_BLOCK) {
+ targetBlock = heap.blocks[i];
+ break;
+ }
+ }
+
+ heap.firstBlockWithPossibleSpace = i;
+
+ if (targetBlock == NULL) {
+ // didn't find one, need to allocate a new block
+
+ if (heap.usedBlocks == heap.numBlocks) {
+ heap.numBlocks = MAX(MIN_ARRAY_SIZE, heap.numBlocks * GROWTH_FACTOR);
+ heap.blocks = (CollectorBlock **)realloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *));
+ }
+
+ targetBlock = (CollectorBlock *)calloc(1, sizeof(CollectorBlock));
+ targetBlock->freeList = targetBlock->cells;
+ heap.blocks[heap.usedBlocks] = targetBlock;
+ heap.usedBlocks++;
+ }
+
+ // find a free spot in the block and detach it from the free list
+ CollectorCell *newCell = targetBlock->freeList;
+
+ ValueImp *imp = (ValueImp*)newCell;
+ if (imp->_vd != NULL) {
+ targetBlock->freeList = (CollectorCell*)(imp->_vd);
+ } else if (targetBlock->usedCells == (CELLS_PER_BLOCK - 1)) {
+ // last cell in this block
+ targetBlock->freeList = NULL;
+ } else {
+ // all next pointers are initially 0, meaning "next cell"
+ targetBlock->freeList = newCell + 1;
+ }
+
+ targetBlock->usedCells++;
+ heap.numLiveObjects++;
+
+ ((ValueImp *)(newCell))->_flags = 0;
+ return (void *)(newCell);
+}
+
+bool Collector::collect()
+{
+ bool deleted = false;
+
+ // MARK: first mark all referenced objects recursively
+ // starting out from the set of root objects
+ if (InterpreterImp::s_hook) {
+ InterpreterImp *scr = InterpreterImp::s_hook;
+ do {
+ //fprintf( stderr, "Collector marking interpreter %p\n",(void*)scr);
+ scr->mark();
+ scr = scr->next;
+ } while (scr != InterpreterImp::s_hook);
+ }
+
+ // mark any other objects that we wouldn't delete anyway
+ for (int block = 0; block < heap.usedBlocks; block++) {
+
+ int minimumCellsToProcess = heap.blocks[block]->usedCells;
+ CollectorBlock *curBlock = heap.blocks[block];
+
+ for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
+ if (minimumCellsToProcess < cell) {
+ goto skip_block_mark;
+ }
+
+ ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
+
+ if (!(imp->_flags & ValueImp::VI_DESTRUCTED)) {
+
+ if ((imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
+ ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
+ imp->mark();
+ }
+ } else {
+ minimumCellsToProcess++;
+ }
+ }
+ skip_block_mark: ;
+ }
+
+ for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
+ ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
+ if ((imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
+ ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
+ imp->mark();
+ }
+ }
+
+ // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else
+
+ int emptyBlocks = 0;
+
+ for (int block = 0; block < heap.usedBlocks; block++) {
+ CollectorBlock *curBlock = heap.blocks[block];
+
+ int minimumCellsToProcess = curBlock->usedCells;
+
+ for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
+ if (minimumCellsToProcess < cell) {
+ goto skip_block_sweep;
+ }
+
+ ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
+
+ if (!(imp->_flags & ValueImp::VI_DESTRUCTED)) {
+ if (!imp->refcount && imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED)) {
+ //fprintf( stderr, "Collector::deleting ValueImp %p (%s)\n", (void*)imp, typeid(*imp).name());
+ // emulate destructing part of 'operator delete()'
+ imp->~ValueImp();
+ curBlock->usedCells--;
+ heap.numLiveObjects--;
+ deleted = true;
+
+ // put it on the free list
+ imp->_vd = (ValueImpPrivate*)curBlock->freeList;
+ curBlock->freeList = (CollectorCell *)imp;
+
+ } else {
+ imp->_flags &= ~ValueImp::VI_MARKED;
+ }
+ } else {
+ minimumCellsToProcess++;
+ }
+ }
+
+ skip_block_sweep:
+
+ if (heap.blocks[block]->usedCells == 0) {
+ emptyBlocks++;
+ if (emptyBlocks > SPARE_EMPTY_BLOCKS) {
+#ifndef DEBUG_COLLECTOR
+ free(heap.blocks[block]);
+#endif
+ // swap with the last block so we compact as we go
+ heap.blocks[block] = heap.blocks[heap.usedBlocks - 1];
+ heap.usedBlocks--;
+ block--; // Don't move forward a step in this case
+
+ if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) {
+ heap.numBlocks = heap.numBlocks / GROWTH_FACTOR;
+ heap.blocks = (CollectorBlock **)realloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *));
+ }
+ }
+ }
+ }
+
+ if (deleted) {
+ heap.firstBlockWithPossibleSpace = 0;
+ }
+
+ int cell = 0;
+ while (cell < heap.usedOversizeCells) {
+ ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
+
+ if (!imp->refcount &&
+ imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED)) {
+
+ imp->~ValueImp();
+#ifndef DEBUG_COLLECTOR
+ free((void *)imp);
+#endif
+
+ // swap with the last oversize cell so we compact as we go
+ heap.oversizeCells[cell] = heap.oversizeCells[heap.usedOversizeCells - 1];
+
+ heap.usedOversizeCells--;
+ deleted = true;
+ heap.numLiveObjects--;
+
+ if (heap.numOversizeCells > MIN_ARRAY_SIZE && heap.usedOversizeCells < heap.numOversizeCells / LOW_WATER_FACTOR) {
+ heap.numOversizeCells = heap.numOversizeCells / GROWTH_FACTOR;
+ heap.oversizeCells = (CollectorCell **)realloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
+ }
+
+ } else {
+ imp->_flags &= ~ValueImp::VI_MARKED;
+ cell++;
+ }
+ }
+
+ heap.numAllocationsSinceLastCollect = 0;
+
+ memoryFull = (heap.numLiveObjects >= KJS_MEM_LIMIT);
+
+ return deleted;
+}
+
+int Collector::size()
+{
+ return heap.numLiveObjects;
+}
+
+#ifdef KJS_DEBUG_MEM
+void Collector::finalCheck()
+{
+}
+#endif
+
+} // namespace KJS
diff --git a/kjs/collector.h b/kjs/collector.h
new file mode 100644
index 000000000..439c14850
--- /dev/null
+++ b/kjs/collector.h
@@ -0,0 +1,74 @@
+// -*- 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 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 _KJSCOLLECTOR_H_
+#define _KJSCOLLECTOR_H_
+
+#define KJS_MEM_LIMIT 500000
+
+#include "global.h"
+
+#include <stdio.h> // for size_t
+
+namespace KJS {
+
+ /**
+ * @short Garbage collector.
+ */
+ class Collector {
+ // disallow direct construction/destruction
+ Collector();
+ public:
+ /**
+ * Register an object with the collector. The following assumptions are
+ * made:
+ * @li the operator new() of the object class is overloaded.
+ * @li operator delete() has been overloaded as well and does not free
+ * the memory on its own.
+ *
+ * @param s Size of the memory to be registered.
+ * @return A pointer to the allocated memory.
+ */
+ static void* allocate(size_t s);
+ /**
+ * Run the garbage collection. This involves calling the delete operator
+ * on each object and freeing the used memory.
+ */
+ static bool collect();
+ static int size();
+ static bool outOfMemory() { return memoryFull; }
+
+#ifdef KJS_DEBUG_MEM
+ /**
+ * Check that nothing is left when the last interpreter gets deleted
+ */
+ static void finalCheck();
+#endif
+
+ private:
+ static bool memoryFull;
+ };
+
+}
+
+#endif /* _KJSCOLLECTOR_H_ */
diff --git a/kjs/completion.h b/kjs/completion.h
new file mode 100644
index 000000000..009ced476
--- /dev/null
+++ b/kjs/completion.h
@@ -0,0 +1,69 @@
+// -*- 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 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_COMPLETION_H_
+#define _KJS_COMPLETION_H_
+
+#include "identifier.h"
+#include "value.h"
+
+namespace KJS {
+
+ /**
+ * Completion types.
+ */
+ enum ComplType { Normal, Break, Continue, ReturnValue, Throw };
+
+ /**
+ * Completion objects are used to convey the return status and value
+ * from functions.
+ *
+ * See FunctionImp::execute()
+ *
+ * @see FunctionImp
+ *
+ * @short Handle for a Completion type.
+ */
+// delme
+ class KJS_EXPORT Completion : public Value {
+// fixme
+/* class Completion : private Value { */
+ public:
+ Completion(ComplType c = Normal, const Value& v = Value(),
+ const Identifier &t = Identifier::null())
+ : comp(c), val(v), tar(t) { }
+
+ ComplType complType() const { return comp; }
+ Value value() const { return val; }
+ Identifier target() const { return tar; }
+ bool isValueCompletion() const { return val.isValid(); }
+ private:
+ ComplType comp;
+ Value val;
+ Identifier tar;
+ };
+
+}
+
+#endif
diff --git a/kjs/configure.in.in b/kjs/configure.in.in
new file mode 100644
index 000000000..1ffa6cedd
--- /dev/null
+++ b/kjs/configure.in.in
@@ -0,0 +1,93 @@
+dnl KDE JavaScript specific configure tests
+
+AC_CHECK_HEADERS(ieeefp.h float.h)
+AC_CHECK_LIB(m, finite, [
+ AC_DEFINE_UNQUOTED(HAVE_FUNC_FINITE, 1, [Define if you have finite])
+])
+AC_CHECK_LIB(m, _finite, [
+ AC_DEFINE_UNQUOTED(HAVE_FUNC__FINITE, 1, [Define if you have _finite])
+])
+
+dnl The C99 standard says that isinf and isnan are macros, but they might
+dnl be functions on some platforms.
+AC_DEFUN([AC_CHECK_ISNAN],
+[
+ ac_save_libs="$LIBS"
+ LIBS="-lm"
+ AC_MSG_CHECKING([for isnan with <math.h>])
+ AC_TRY_LINK(
+ [#include <math.h>
+ float f;], [return isnan(f)],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_FUNC_ISNAN], [1], [Define if you have isnan])],
+ AC_MSG_RESULT(no)
+ )
+ LIBS="$ac_save_libs"
+])
+AC_DEFUN([AC_CHECK_ISINF],
+[
+ ac_save_libs="$LIBS"
+ LIBS="-lm"
+ AC_MSG_CHECKING([for isinf with <math.h>])
+ AC_TRY_LINK(
+ [#include <math.h>
+ float f;], [return isinf(f)],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE([HAVE_FUNC_ISINF], [1], [Define if you have isinf])],
+ AC_MSG_RESULT(no)
+ )
+ LIBS="$ac_save_libs"
+])
+
+AC_CHECK_ISNAN
+AC_CHECK_ISINF
+
+AC_DEFUN([AC_CHECK_PCREPOSIX],
+[
+ dnl define the configure option that disables pcre
+ AC_ARG_ENABLE(pcre,AC_HELP_STRING([--disable-pcre],[don't require libpcre (poor RegExp support in Javascript)]),
+ with_pcre=$enableval, with_pcre=yes)
+
+ if test "$with_pcre" = "yes"; then
+
+ KDE_FIND_PATH(pcre-config, PCRE_CONFIG, [${exec_prefix}/bin ${prefix}/bin], [PCRE_CONFIG="" ])
+ if test -n "$PCRE_CONFIG" && $PCRE_CONFIG --libs >/dev/null 2>&1; then
+ LIBPCRE=`$PCRE_CONFIG --libs-posix | sed -e "s,-L/usr/lib ,,"`
+ PCRECFLAGS=`$PCRE_CONFIG --cflags`
+ else
+ LIBPCRE="-lpcre -lpcreposix"
+ PCRECFLAGS=
+ fi
+ AC_CACHE_VAL(ac_cv_have_pcreposix, [
+ ac_save_libs="$LIBS"
+ LIBS="$LIBPCRE"
+ ac_CPPFLAGS_save="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $PCRECFLAGS $all_includes"
+ ac_LDFLAGS_save="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $all_libraries"
+ AC_TRY_LINK(
+ [#include <pcre.h>],
+ [regfree(0);],
+ [ac_cv_have_pcreposix="yes"],
+ [ac_cv_have_pcreposix="no"]
+ )
+ LIBS="$ac_save_libs"
+ LDFLAGS="$ac_LDFLAGS_save"
+ CPPFLAGS="$ac_CPPFLAGS_save"
+ ])
+ if test "$ac_cv_have_pcreposix" = "yes"; then
+ AC_DEFINE(HAVE_PCREPOSIX, 1, [Define if you have pcreposix libraries and header files.])
+ else
+ AC_MSG_ERROR([You're missing libpcre.
+Download libpcre from http://www.pcre.org or find a binary package for your platform.
+Alternatively, you can specify --disable-pcre, but some web pages - using regular
+expressions in Javascript code - will not work correctly, the regexp support being
+quite limited if libpcre isn't present.])
+ fi
+ fi
+])
+AC_CHECK_PCREPOSIX
+AC_SUBST(LIBPCRE)
+AC_SUBST(PCRECFLAGS)
+
+AM_CONFIG_HEADER([kjs/global.h])
diff --git a/kjs/context.h b/kjs/context.h
new file mode 100644
index 000000000..1b435798e
--- /dev/null
+++ b/kjs/context.h
@@ -0,0 +1,90 @@
+// -*- 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 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_CONTEXT_H
+#define KJS_CONTEXT_H
+
+#include "function.h"
+
+namespace KJS {
+
+ /**
+ * @short Execution context.
+ */
+ class ContextImp {
+ friend class Context;
+ friend class StatementNode;
+ public:
+ // TODO: remove glob parameter. deducable from exec.
+ ContextImp(Object &glob, InterpreterImp *interpreter, Object &thisV, int _sourceId, CodeType type = GlobalCode,
+ ContextImp *callingContext = 0L, FunctionImp *func = 0L, const List *args = 0);
+ virtual ~ContextImp();
+
+ const ScopeChain &scopeChain() const { return scope; }
+ CodeType codeType() const { return m_codeType; }
+ Object variableObject() const { return variable; }
+ void setVariableObject(const Object &v) { variable = v; }
+ Object thisValue() const { return thisVal; }
+ ContextImp *callingContext() { return _callingContext; }
+ ObjectImp *activationObject() { return activation.imp(); }
+ FunctionImp *function() const { return _function; }
+ const List *arguments() const { return _arguments; }
+
+ void pushScope(const Object &s) { scope.push(s.imp()); }
+ void popScope() { scope.pop(); }
+ LabelStack *seenLabels() { return &ls; }
+
+ void mark();
+
+ void pushTryCatch() { tryCatch++; };
+ void popTryCatch() { tryCatch--; };
+ bool inTryCatch() const;
+
+ void setLines(int l0, int l1) { line0 = l0; line1 = l1; }
+
+ private:
+ InterpreterImp *_interpreter;
+ ContextImp *_callingContext;
+ FunctionImp *_function;
+ const List *_arguments;
+ Object activation;
+
+ ScopeChain scope;
+ Object variable;
+ Object thisVal;
+
+ LabelStack ls;
+ CodeType m_codeType;
+
+ int tryCatch;
+ int sourceId;
+ int line0;
+ int line1;
+ Identifier functionName;
+ List args;
+ };
+
+} // namespace KJS
+
+#endif
diff --git a/kjs/create_hash_table b/kjs/create_hash_table
new file mode 100755
index 000000000..a7df5eddf
--- /dev/null
+++ b/kjs/create_hash_table
@@ -0,0 +1,203 @@
+#! /usr/bin/perl -w
+#
+# Static Hashtable Generator
+#
+# (c) 2000-2002 by Harri Porten <porten@kde.org> and
+# David Faure <faure@kde.org>
+
+$file = $ARGV[0];
+shift;
+my $findSize = 0;
+my $includelookup = 0;
+# Use -s as second argument to make it try many hash sizes
+$findSize = 1 if (defined($ARGV[0]) && $ARGV[0] eq "-s");
+# Use -i as second argument to make it include "lookup.h"
+$includelookup = 1 if (defined($ARGV[0]) && $ARGV[0] eq "-i");
+print STDERR "Creating hashtable for $file\n";
+open(IN, $file) or die "No such file $file";
+
+@keys = ();
+@values = ();
+@attrs = ();
+@params = ();
+
+my $inside = 0;
+my $name;
+my $size;
+my $hashsize;
+my $banner = 0;
+my $namespace = "KJS";
+
+sub calcTable();
+sub output();
+sub hashValue($);
+
+while (<IN>) {
+ chop;
+ s/^\s*//g;
+ if (/^\#|^$/) {
+ # comment. do nothing
+ } elsif (/^\@namespace\s+(\w+)/ && !$inside) {
+ $namespace = $1;
+ } elsif (/^\@begin/ && !$inside) {
+ if (/^\@begin\s*([:_\w]+)\s*(\d+)\s*$/) {
+ $inside = 1;
+ $name = $1;
+ $hashsize = $2;
+ } else {
+ printf STDERR "WARNING: \@begin without table name and hashsize, skipping $_\n";
+ }
+ } elsif (/^\@end\s*$/ && $inside) {
+
+ if($findSize) {
+ my $entriesnum=@keys;
+ print STDERR "Table: $name $entriesnum entries\n";
+ for( $i=3 ; $i<79 ; ++$i) { $hashsize=$i ; calcTable(); }
+ } else {
+ calcTable();
+ }
+
+ output();
+ @keys = ();
+ @values = ();
+ @attrs = ();
+ @params = ();
+ $inside = 0;
+ } elsif (/^([-:\@\w\[\=\]]+)\s*([\w\:-]+)\s*([\w\|]*)\s*(\w*)\s*$/ && $inside) {
+ my $key = $1;
+ my $val = $2;
+ my $att = $3;
+ my $param = $4;
+ push(@keys, $key);
+ push(@values, $val);
+ printf STDERR "WARNING: Number of arguments missing for $key/$val\n"
+ if ( $att =~ m/Function/ && length($param) == 0);
+ push(@attrs, length($att) > 0 ? $att : "0");
+ push(@params, length($param) > 0 ? $param : "0");
+ } elsif ($inside) {
+ die "invalid data";
+ }
+}
+
+die "missing closing \@end" if ($inside);
+
+sub calcTable() {
+ @table = ();
+ @links = ();
+ $size = $hashsize;
+ my $collisions = 0;
+ my $maxdepth = 0;
+ my $i = 0;
+ foreach $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++;
+ }
+ }
+ #print STDERR "table[$h] = $i\n";
+ $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;
+ }
+ #print STDERR "After loop: size=$size table=".($#table+1)."\n";
+
+ if ($findSize) {
+ my $emptycount = 0;
+ foreach $entry (@table) {
+ $emptycount++ if (!defined($entry));
+ }
+ print STDERR "Hashsize: $hashsize Total Size: $size Empty: $emptycount MaxDepth: $maxdepth Collisions: $collisions\n";
+ }
+# my $debugtable = 0;
+# foreach $entry (@table) {
+# print STDERR "$debugtable " . (defined $entry ? $entry : '<undefined>');
+# print STDERR " -> " . $links[$debugtable] if (defined($links[$debugtable]));
+# print STDERR "\n";
+# $debugtable++;
+# }
+}
+
+sub hashValue($) {
+ @chars = split(/ */, $_[0]);
+ my $val = 0;
+ foreach $c (@chars) {
+ $val += ord($c);
+ }
+ return $val;
+}
+
+sub output() {
+ if (!$banner) {
+ $banner = 1;
+ print "/* Automatically generated from $file using $0. DO NOT EDIT ! */\n";
+ }
+
+ my $nameEntries = "${name}Entries";
+ $nameEntries =~ s/:/_/g;
+ my $nameStringTable = "${name}Strings";
+ $nameStringTable =~ y/:/_/;
+
+ print "\n#include \"lookup.h\"\n" if ($includelookup);
+ print "\nusing namespace KJS;\n"; # because of DontDelete etc.
+ print "\nnamespace $namespace {\n";
+
+ # first, build the string table
+ my %soffset = ();
+ print "\nstatic const char $nameStringTable\[\] = {\n";
+ my $s = "\0";
+ print " \"\\0\"\n";
+ for my $k (sort { length $b <=> length $a || $a cmp $b } @keys) {
+ if ($s =~ /^(.*)\Q$k\E\0/) {
+ $soffset{$k} = length $1;
+ } else {
+ $soffset{$k} = length $s;
+ print " \"$k\\0\"\n";
+ $s .= $k;
+ $s .= "\0";
+ }
+ }
+ print "};\n\n";
+
+ # now, dump the hash table
+
+ print "\nstatic const struct HashEntry ".$nameEntries."[] = {\n";
+ my $i = 0;
+ #print STDERR "writing out table with ".($#table+1)." entries\n";
+ foreach $entry (@table) {
+ if (defined($entry)) {
+ my $key = $keys[$entry];
+ print " \{ " . $soffset{$key};
+ print ", " . $values[$entry];
+ print ", " . $attrs[$entry];
+ print ", " . $params[$entry];
+ print ", ";
+ if (defined($links[$i])) {
+ print $links[$i] . " \}";
+ } else {
+ print "-1 \}"
+ }
+ } else {
+ print " \{ 0, 0, 0, 0, -1 \}";
+ }
+ print "," unless ($i == $size - 1);
+ print "\n";
+ $i++;
+ }
+ print "};\n\n";
+ print "const struct HashTable $name = ";
+ print "\{ 2, $size, ".$nameEntries.", $hashsize, ".$nameStringTable."\};\n\n";
+ print "} // namespace\n";
+}
diff --git a/kjs/date_object.cpp b/kjs/date_object.cpp
new file mode 100644
index 000000000..26635611d
--- /dev/null
+++ b/kjs/date_object.cpp
@@ -0,0 +1,1214 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2005 Harri Porten (porten@kde.org)
+ * Copyright (C) 2004 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+# include <time.h>
+# endif
+#endif
+#ifdef HAVE_SYS_TIMEB_H
+#include <sys/timeb.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif // HAVE_SYS_PARAM_H
+
+#include <math.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "date_object.h"
+#include "error_object.h"
+#include "operations.h"
+
+#include "date_object.lut.h"
+
+#ifdef _MSC_VER
+# define strncasecmp(a,b,c) _strnicmp(a,b,c)
+#endif
+
+using namespace KJS;
+
+// come constants
+const time_t invalidDate = LONG_MIN;
+const double hoursPerDay = 24;
+const double minutesPerHour = 60;
+const double secondsPerMinute = 60;
+const double msPerSecond = 1000;
+const double msPerMinute = msPerSecond * secondsPerMinute;
+const double msPerHour = msPerMinute * minutesPerHour;
+const double msPerDay = msPerHour * hoursPerDay;
+static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+static UString formatDate(struct tm &tm)
+{
+ char buffer[100];
+ snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
+ weekdayName[(tm.tm_wday + 6) % 7],
+ monthName[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
+ return buffer;
+}
+
+static UString formatDateUTCVariant(struct tm &tm)
+{
+ char buffer[100];
+ snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
+ weekdayName[(tm.tm_wday + 6) % 7],
+ tm.tm_mday, monthName[tm.tm_mon], tm.tm_year + 1900);
+ return buffer;
+}
+
+static UString formatTime(struct tm &tm)
+{
+ int tz;
+ char buffer[100];
+#if defined BSD || defined(__linux__) || defined(__APPLE__)
+ tz = tm.tm_gmtoff;
+#else
+# if defined(__BORLANDC__) || defined (__CYGWIN__)
+ tz = - _timezone;
+# else
+ tz = - timezone;
+# endif
+#endif
+ if (tz == 0) {
+ snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
+ } else {
+ int offset = tz;
+ if (offset < 0) {
+ offset = -offset;
+ }
+ snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tz < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
+ }
+ return UString(buffer);
+}
+
+static int day(double t)
+{
+ return int(floor(t / msPerDay));
+}
+
+static double dayFromYear(int year)
+{
+ return 365.0 * (year - 1970)
+ + floor((year - 1969) / 4.0)
+ - floor((year - 1901) / 100.0)
+ + floor((year - 1601) / 400.0);
+}
+
+// depending on whether it's a leap year or not
+static int daysInYear(int year)
+{
+ if (year % 4 != 0)
+ return 365;
+ else if (year % 400 == 0)
+ return 366;
+ else if (year % 100 == 0)
+ return 365;
+ else
+ return 366;
+}
+
+// time value of the start of a year
+double timeFromYear(int year)
+{
+ return msPerDay * dayFromYear(year);
+}
+
+// year determined by time value
+int yearFromTime(double t)
+{
+ // ### there must be an easier way
+ // initial guess
+ int y = 1970 + int(t / (365.25 * msPerDay));
+ // adjustment
+ if (timeFromYear(y) > t) {
+ do {
+ --y;
+ } while (timeFromYear(y) > t);
+ } else {
+ while (timeFromYear(y + 1) < t)
+ ++y;
+ }
+
+ return y;
+}
+
+// 0: Sunday, 1: Monday, etc.
+int weekDay(double t)
+{
+ int wd = (day(t) + 4) % 7;
+ if (wd < 0)
+ wd += 7;
+ return wd;
+}
+
+static double timeZoneOffset(const struct tm *t)
+{
+#if defined BSD || defined(__linux__) || defined(__APPLE__)
+ return -(t->tm_gmtoff / 60);
+#else
+# if defined(__BORLANDC__) || defined(__CYGWIN__)
+// FIXME consider non one-hour DST change
+#if !defined(__CYGWIN__)
+#error please add daylight savings offset here!
+#endif
+ return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
+# else
+ return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
+# endif
+#endif
+}
+
+// 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, struct tm *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->tm_hour = 0;
+ milliseconds += args[idx++].toInt32(exec) * msPerHour;
+ }
+
+ // minutes
+ if (maxArgs >= 3 && idx < numArgs) {
+ t->tm_min = 0;
+ milliseconds += args[idx++].toInt32(exec) * msPerMinute;
+ }
+
+ // seconds
+ if (maxArgs >= 2 && idx < numArgs) {
+ t->tm_sec = 0;
+ milliseconds += args[idx++].toInt32(exec) * msPerSecond;
+ }
+
+ // milliseconds
+ if (idx < numArgs) {
+ milliseconds += roundValue(exec, args[idx]);
+ } 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, struct tm *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->tm_year = args[idx++].toInt32(exec) - 1900;
+ }
+
+ // months
+ if (maxArgs >= 2 && idx < numArgs) {
+ t->tm_mon = args[idx++].toInt32(exec);
+ }
+
+ // days
+ if (idx < numArgs) {
+ t->tm_mday = 0;
+ *ms += args[idx].toInt32(exec) * msPerDay;
+ }
+}
+
+// ------------------------------ DateInstanceImp ------------------------------
+
+const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
+
+DateInstanceImp::DateInstanceImp(ObjectImp *proto)
+ : ObjectImp(proto)
+{
+}
+
+// ------------------------------ DatePrototypeImp -----------------------------
+
+const ClassInfo DatePrototypeImp::info = {"Date", &DateInstanceImp::info, &dateTable, 0};
+
+/* Source for date_object.lut.h
+ We use a negative ID to denote the "UTC" variant.
+@begin dateTable 61
+ toString DateProtoFuncImp::ToString DontEnum|Function 0
+ toUTCString DateProtoFuncImp::ToUTCString DontEnum|Function 0
+ toDateString DateProtoFuncImp::ToDateString DontEnum|Function 0
+ toTimeString DateProtoFuncImp::ToTimeString DontEnum|Function 0
+ toLocaleString DateProtoFuncImp::ToLocaleString DontEnum|Function 0
+ toLocaleDateString DateProtoFuncImp::ToLocaleDateString DontEnum|Function 0
+ toLocaleTimeString DateProtoFuncImp::ToLocaleTimeString DontEnum|Function 0
+ valueOf DateProtoFuncImp::ValueOf DontEnum|Function 0
+ getTime DateProtoFuncImp::GetTime DontEnum|Function 0
+ getFullYear DateProtoFuncImp::GetFullYear DontEnum|Function 0
+ getUTCFullYear -DateProtoFuncImp::GetFullYear DontEnum|Function 0
+ toGMTString DateProtoFuncImp::ToGMTString DontEnum|Function 0
+ getMonth DateProtoFuncImp::GetMonth DontEnum|Function 0
+ getUTCMonth -DateProtoFuncImp::GetMonth DontEnum|Function 0
+ getDate DateProtoFuncImp::GetDate DontEnum|Function 0
+ getUTCDate -DateProtoFuncImp::GetDate DontEnum|Function 0
+ getDay DateProtoFuncImp::GetDay DontEnum|Function 0
+ getUTCDay -DateProtoFuncImp::GetDay DontEnum|Function 0
+ getHours DateProtoFuncImp::GetHours DontEnum|Function 0
+ getUTCHours -DateProtoFuncImp::GetHours DontEnum|Function 0
+ getMinutes DateProtoFuncImp::GetMinutes DontEnum|Function 0
+ getUTCMinutes -DateProtoFuncImp::GetMinutes DontEnum|Function 0
+ getSeconds DateProtoFuncImp::GetSeconds DontEnum|Function 0
+ getUTCSeconds -DateProtoFuncImp::GetSeconds DontEnum|Function 0
+ getMilliseconds DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
+ getUTCMilliseconds -DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
+ getTimezoneOffset DateProtoFuncImp::GetTimezoneOffset DontEnum|Function 0
+ setTime DateProtoFuncImp::SetTime DontEnum|Function 1
+ setMilliseconds DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
+ setUTCMilliseconds -DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
+ setSeconds DateProtoFuncImp::SetSeconds DontEnum|Function 2
+ setUTCSeconds -DateProtoFuncImp::SetSeconds DontEnum|Function 2
+ setMinutes DateProtoFuncImp::SetMinutes DontEnum|Function 3
+ setUTCMinutes -DateProtoFuncImp::SetMinutes DontEnum|Function 3
+ setHours DateProtoFuncImp::SetHours DontEnum|Function 4
+ setUTCHours -DateProtoFuncImp::SetHours DontEnum|Function 4
+ setDate DateProtoFuncImp::SetDate DontEnum|Function 1
+ setUTCDate -DateProtoFuncImp::SetDate DontEnum|Function 1
+ setMonth DateProtoFuncImp::SetMonth DontEnum|Function 2
+ setUTCMonth -DateProtoFuncImp::SetMonth DontEnum|Function 2
+ setFullYear DateProtoFuncImp::SetFullYear DontEnum|Function 3
+ setUTCFullYear -DateProtoFuncImp::SetFullYear DontEnum|Function 3
+ setYear DateProtoFuncImp::SetYear DontEnum|Function 1
+ getYear DateProtoFuncImp::GetYear DontEnum|Function 0
+ toGMTString DateProtoFuncImp::ToGMTString DontEnum|Function 0
+@end
+*/
+// ECMA 15.9.4
+
+DatePrototypeImp::DatePrototypeImp(ExecState *,
+ ObjectPrototypeImp *objectProto)
+ : DateInstanceImp(objectProto)
+{
+ Value protect(this);
+ setInternalValue(Number(NaN));
+ // The constructor will be added later, after DateObjectImp has been built
+}
+
+Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
+}
+
+// ------------------------------ DateProtoFuncImp -----------------------------
+
+DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
+ ), id(abs(i)), utc(i<0)
+ // We use a negative ID to denote the "UTC" variant.
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+}
+
+bool DateProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ if (!thisObj.inherits(&DateInstanceImp::info)) {
+ // non-generic function called on non-date object
+
+ // ToString and ValueOf are generic according to the spec, but the mozilla
+ // tests suggest otherwise...
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+
+ Value result;
+ UString s;
+ const int bufsize=100;
+ char timebuffer[bufsize];
+ CString oldlocale = setlocale(LC_TIME,NULL);
+ if (!oldlocale.c_str())
+ oldlocale = setlocale(LC_ALL, NULL);
+ Value v = thisObj.internalValue();
+ double milli = v.toNumber(exec);
+ // special case: time value is NaN
+ if (isNaN(milli)) {
+ switch (id) {
+ case ToString:
+ case ToDateString:
+ case ToTimeString:
+ case ToGMTString:
+ case ToUTCString:
+ case ToLocaleString:
+ case ToLocaleDateString:
+ case ToLocaleTimeString:
+ return String("Invalid Date");
+ case ValueOf:
+ case GetTime:
+ case GetYear:
+ case GetFullYear:
+ case GetMonth:
+ case GetDate:
+ case GetDay:
+ case GetHours:
+ case GetMinutes:
+ case GetSeconds:
+ case GetMilliSeconds:
+ case GetTimezoneOffset:
+ case SetMilliSeconds:
+ case SetSeconds:
+ case SetMinutes:
+ case SetHours:
+ case SetDate:
+ case SetMonth:
+ case SetFullYear:
+ return Number(NaN);
+ }
+ }
+
+ if (id == SetTime) {
+ result = Number(roundValue(exec,args[0]));
+ thisObj.setInternalValue(result);
+ return result;
+ }
+
+ // check whether time value is outside time_t's usual range
+ // make the necessary transformations if necessary
+ int realYearOffset = 0;
+ double milliOffset = 0.0;
+ if (milli < 0 || milli >= timeFromYear(2038)) {
+ // ### ugly and probably not very precise
+ int realYear = yearFromTime(milli);
+ int base = daysInYear(realYear) == 365 ? 2001 : 2000;
+ milliOffset = timeFromYear(base) - timeFromYear(realYear);
+ milli += milliOffset;
+ realYearOffset = realYear - base;
+ }
+
+ time_t tv = (time_t) floor(milli / 1000.0);
+ double ms = milli - tv * 1000.0;
+
+ struct tm *t;
+ if ( (id == DateProtoFuncImp::ToGMTString) ||
+ (id == DateProtoFuncImp::ToUTCString) )
+ t = gmtime(&tv);
+ else if (id == DateProtoFuncImp::ToString)
+ t = localtime(&tv);
+ else if (utc)
+ t = gmtime(&tv);
+ else
+ t = localtime(&tv);
+
+ // we had an out of range year. use that one (plus/minus offset
+ // found by calculating tm_year) and fix the week day calculation
+ if (realYearOffset != 0) {
+ t->tm_year += realYearOffset;
+ milli -= milliOffset;
+ // our own weekday calculation. beware of need for local time.
+ double m = milli;
+ if (!utc)
+ m -= timeZoneOffset(t) * msPerMinute;
+ t->tm_wday = weekDay(m);
+ }
+
+ // trick gcc. We don't want the Y2K warnings.
+ const char xFormat[] = "%x";
+ const char cFormat[] = "%c";
+
+ switch (id) {
+ case ToString:
+ result = String(formatDate(*t) + " " + formatTime(*t));
+ break;
+ case ToDateString:
+ result = String(formatDate(*t));
+ break;
+ case ToTimeString:
+ result = String(formatTime(*t));
+ break;
+ case ToGMTString:
+ case ToUTCString:
+ result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
+ break;
+ case ToLocaleString:
+ strftime(timebuffer, bufsize, cFormat, t);
+ result = String(timebuffer);
+ break;
+ case ToLocaleDateString:
+ strftime(timebuffer, bufsize, xFormat, t);
+ result = String(timebuffer);
+ break;
+ case ToLocaleTimeString:
+ strftime(timebuffer, bufsize, "%X", t);
+ result = String(timebuffer);
+ break;
+ case ValueOf:
+ result = Number(milli);
+ break;
+ case GetTime:
+ result = Number(milli);
+ break;
+ case GetYear:
+ // IE returns the full year even in getYear.
+ if ( exec->dynamicInterpreter()->compatMode() != Interpreter::IECompat )
+ result = Number(t->tm_year);
+ else
+ result = Number(1900 + t->tm_year);
+ break;
+ case GetFullYear:
+ result = Number(1900 + t->tm_year);
+ break;
+ case GetMonth:
+ result = Number(t->tm_mon);
+ break;
+ case GetDate:
+ result = Number(t->tm_mday);
+ break;
+ case GetDay:
+ result = Number(t->tm_wday);
+ break;
+ case GetHours:
+ result = Number(t->tm_hour);
+ break;
+ case GetMinutes:
+ result = Number(t->tm_min);
+ break;
+ case GetSeconds:
+ result = Number(t->tm_sec);
+ break;
+ case GetMilliSeconds:
+ result = Number(ms);
+ break;
+ case GetTimezoneOffset:
+ result = Number(timeZoneOffset(t));
+ break;
+ case SetMilliSeconds:
+ fillStructuresUsingTimeArgs(exec, args, 1, &ms, t);
+ break;
+ case SetSeconds:
+ fillStructuresUsingTimeArgs(exec, args, 2, &ms, t);
+ break;
+ case SetMinutes:
+ fillStructuresUsingTimeArgs(exec, args, 3, &ms, t);
+ break;
+ case SetHours:
+ fillStructuresUsingTimeArgs(exec, args, 4, &ms, t);
+ break;
+ case SetDate:
+ fillStructuresUsingDateArgs(exec, args, 1, &ms, t);
+ break;
+ case SetMonth:
+ fillStructuresUsingDateArgs(exec, args, 2, &ms, t);
+ break;
+ case SetFullYear:
+ fillStructuresUsingDateArgs(exec, args, 3, &ms, t);
+ break;
+ case SetYear:
+ int y = args[0].toInt32(exec);
+ if (y < 1900) {
+ if (y >= 0 && y <= 99) {
+ t->tm_year = y;
+ } else {
+ fillStructuresUsingDateArgs(exec, args, 3, &ms, t);
+ }
+ } else {
+ t->tm_year = y - 1900;
+ }
+ break;
+ }
+
+ if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
+ id == SetMinutes || id == SetHours || id == SetDate ||
+ id == SetMonth || id == SetFullYear ) {
+ result = Number(makeTime(t, ms, utc));
+ thisObj.setInternalValue(result);
+ }
+
+ return result;
+}
+
+// ------------------------------ DateObjectImp --------------------------------
+
+// TODO: MakeTime (15.9.11.1) etc. ?
+
+DateObjectImp::DateObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ DatePrototypeImp *dateProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+
+ // ECMA 15.9.4.1 Date.prototype
+ putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
+
+ static const Identifier parsePropertyName("parse");
+ putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
+ static const Identifier UTCPropertyName("UTC");
+ putDirect(UTCPropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC, 7), DontEnum);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
+}
+
+bool DateObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.9.3
+Object DateObjectImp::construct(ExecState *exec, const List &args)
+{
+ int numArgs = args.size();
+
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
+#endif
+ double value;
+
+ if (numArgs == 0) { // new Date() ECMA 15.9.3.3
+#ifdef HAVE_SYS_TIMEB_H
+# if defined(__BORLANDC__)
+ struct timeb timebuffer;
+ ftime(&timebuffer);
+# else
+ struct _timeb timebuffer;
+ _ftime(&timebuffer);
+# endif
+ double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, 0L);
+ double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
+#endif
+ value = utc;
+ } else if (numArgs == 1) {
+ Value prim = args[0].toPrimitive(exec);
+ if (prim.isA(StringType))
+ value = parseDate(prim.toString(exec));
+ else
+ value = prim.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 {
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ int year = args[0].toInt32(exec);
+ t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
+ t.tm_mon = args[1].toInt32(exec);
+ t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
+ t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
+ t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
+ t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
+ t.tm_isdst = -1;
+ int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
+ value = makeTime(&t, ms, false);
+ }
+ }
+
+ Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
+ Object ret(new DateInstanceImp(proto.imp()));
+ ret.setInternalValue(Number(timeClip(value)));
+ return ret;
+}
+
+bool DateObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.9.2
+Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
+{
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"DateObjectImp::call - current time\n");
+#endif
+ time_t t = time(0L);
+ // FIXME: not threadsafe
+ struct tm *tm = localtime(&t);
+ return String(formatDate(*tm) + " " + formatTime(*tm));
+}
+
+// ------------------------------ DateObjectFuncImp ----------------------------
+
+DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
+ int i, int len)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+}
+
+bool DateObjectFuncImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.9.4.2 - 3
+Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ if (id == Parse) {
+ return Number(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 Number(NaN);
+ }
+
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ int year = args[0].toInt32(exec);
+ t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
+ t.tm_mon = args[1].toInt32(exec);
+ t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
+ t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
+ t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
+ t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
+ int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
+ return Number(makeTime(&t, ms, true));
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+
+double KJS::parseDate(const UString &u)
+{
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
+#endif
+ double /*time_t*/ seconds = KRFCDate_parseDate( u );
+
+ return seconds == invalidDate ? NaN : seconds * 1000.0;
+}
+
+///// Awful duplication from krfcdate.cpp - we don't link to kdecore
+
+static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
+{
+ //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
+
+ double ret = (day - 32075) /* days */
+ + 1461L * (year + 4800L + (mon - 14) / 12) / 4
+ + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
+ - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
+ - 2440588;
+ ret = 24*ret + hour; /* hours */
+ ret = 60*ret + minute; /* minutes */
+ ret = 60*ret + second; /* seconds */
+
+ return ret;
+}
+
+// we follow the recommendation of rfc2822 to consider all
+// obsolete time zones not listed here equivalent to "-0000"
+static const struct KnownZone {
+#ifdef _WIN32
+ char tzName[4];
+#else
+ const char tzName[4];
+#endif
+ int tzOffset;
+} known_zones[] = {
+ { "UT", 0 },
+ { "GMT", 0 },
+ { "EST", -300 },
+ { "EDT", -240 },
+ { "CST", -360 },
+ { "CDT", -300 },
+ { "MST", -420 },
+ { "MDT", -360 },
+ { "PST", -480 },
+ { "PDT", -420 }
+};
+
+double KJS::makeTime(struct tm *t, double ms, bool utc)
+{
+ int utcOffset;
+ if (utc) {
+ time_t zero = 0;
+#if defined BSD || defined(__linux__) || defined(__APPLE__)
+ struct tm t3;
+ localtime_r(&zero, &t3);
+ utcOffset = t3.tm_gmtoff;
+ t->tm_isdst = t3.tm_isdst;
+#else
+ (void)localtime(&zero);
+# if defined(__BORLANDC__) || defined(__CYGWIN__)
+ utcOffset = - _timezone;
+# else
+ utcOffset = - timezone;
+# endif
+ t->tm_isdst = 0;
+#endif
+ } else {
+ utcOffset = 0;
+ t->tm_isdst = -1;
+ }
+
+ double yearOffset = 0.0;
+ if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
+ // we'll fool mktime() into believing that this year is within
+ // its normal, portable range (1970-2038) by setting tm_year to
+ // 2000 or 2001 and adding the difference in milliseconds later.
+ // choice between offset will depend on whether the year is a
+ // leap year or not.
+ int y = t->tm_year + 1900;
+ int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
+ const double baseTime = timeFromYear(baseYear);
+ yearOffset = timeFromYear(y) - baseTime;
+ t->tm_year = baseYear - 1900;
+ }
+
+ // Determine if we passed over a DST change boundary
+ if (!utc) {
+ time_t tval = mktime(t) + utcOffset + int((ms + yearOffset)/1000);
+ struct tm t3;
+ localtime_r(&tval, &t3);
+ t->tm_isdst = t3.tm_isdst;
+ }
+
+ return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
+}
+
+// returns 0-11 (Jan-Dec); -1 on failure
+static int findMonth(const char *monthStr)
+{
+ assert(monthStr);
+ static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
+ char needle[4];
+ for (int i = 0; i < 3; ++i) {
+ if (!*monthStr)
+ return -1;
+ needle[i] = tolower(*monthStr++);
+ }
+ needle[3] = '\0';
+ const char *str = strstr(haystack, needle);
+ if (str) {
+ int position = str - haystack;
+ if (position % 3 == 0) {
+ return position / 3;
+ }
+ }
+ return -1;
+}
+
+// maybe this should be more often than just isspace() but beware of
+// conflicts with : in time strings.
+static bool isSpaceLike(char c)
+{
+ return isspace(c) || c == ',' || c == ':' || c == '-';
+}
+
+double KJS::KRFCDate_parseDate(const UString &_date)
+{
+ // This parse a date in the form:
+ // Wednesday, 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
+ //
+ double result = -1;
+ int offset = 0;
+ bool have_tz = false;
+ char *newPosStr;
+ const char *dateString = _date.ascii();
+ int day = 0;
+ int month = -1; // not set yet
+ int year = 0;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ bool have_time = false;
+
+ // Skip leading space
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+
+ const char *wordStart = dateString;
+ // Check contents of first words if not number
+ while(*dateString && !isdigit(*dateString))
+ {
+ if (isSpaceLike(*dateString) && dateString - wordStart >= 3)
+ {
+ month = findMonth(wordStart);
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+ wordStart = dateString;
+ }
+ else
+ dateString++;
+ }
+ // missing delimiter between month and day (like "January29")?
+ if (month == -1 && dateString && wordStart != dateString) {
+ month = findMonth(wordStart);
+ // TODO: emit warning about dubious format found
+ }
+
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return invalidDate;
+
+ // ' 09-Nov-99 23:12:40 GMT'
+ errno = 0;
+ day = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if (!*dateString)
+ return invalidDate;
+
+ if (day < 0)
+ return invalidDate;
+ if (day > 31) {
+ // ### where is the boundary and what happens below?
+ if (*dateString == '/') {
+ // looks like a YYYY/MM/DD date
+ if (!*++dateString)
+ return invalidDate;
+ year = day;
+ month = strtol(dateString, &newPosStr, 10) - 1;
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ if (*dateString++ != '/' || !*dateString)
+ return invalidDate;
+ day = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ } else {
+ return invalidDate;
+ }
+ } 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 invalidDate;
+ dateString = newPosStr;
+ if (*dateString == '/')
+ dateString++;
+ if (!*dateString)
+ return invalidDate;
+ //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
+ }
+ else
+ {
+ if (*dateString == '-')
+ dateString++;
+
+ while(*dateString && isSpaceLike(*dateString))
+ dateString++;
+
+ if (*dateString == ',')
+ dateString++;
+
+ if ( month == -1 ) // not found yet
+ {
+ month = findMonth(dateString);
+ if (month == -1)
+ return invalidDate;
+
+ while(*dateString && (*dateString != '-') && !isSpaceLike(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return invalidDate;
+
+ // '-99 23:12:40 GMT'
+ if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
+ return invalidDate;
+ dateString++;
+ }
+
+ if ((month < 0) || (month > 11))
+ return invalidDate;
+ }
+
+ // '99 23:12:40 GMT'
+ if (year <= 0 && *dateString) {
+ year = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ }
+
+ // Don't fail if the time is missing.
+ if (*newPosStr)
+ {
+ // ' 23:12:40 GMT'
+ if (*newPosStr == ':') // Ah, so there was no year, but the number was the hour
+ year = -1;
+ else if (isSpaceLike(*newPosStr)) // we parsed the year
+ dateString = ++newPosStr;
+ else
+ return invalidDate;
+
+ 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) {
+ have_time = true;
+ dateString = newPosStr;
+
+ if ((hour < 0) || (hour > 23))
+ return invalidDate;
+
+ if (!*dateString)
+ return invalidDate;
+
+ // ':12:40 GMT'
+ if (*dateString++ != ':')
+ return invalidDate;
+
+ minute = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if ((minute < 0) || (minute > 59))
+ return invalidDate;
+
+ // ':40 GMT'
+ if (*dateString && *dateString != ':' && !isspace(*dateString))
+ return invalidDate;
+
+ // seconds are optional in rfc822 + rfc2822
+ if (*dateString ==':') {
+ dateString++;
+
+ second = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if ((second < 0) || (second > 59))
+ return invalidDate;
+
+ // disallow trailing colon seconds
+ if (*dateString == ':')
+ return invalidDate;
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if (strncasecmp(dateString, "AM", 2) == 0) {
+ if (hour > 12)
+ return invalidDate;
+ if (hour == 12)
+ hour = 0;
+ dateString += 2;
+ while (isspace(*dateString))
+ dateString++;
+ } else if (strncasecmp(dateString, "PM", 2) == 0) {
+ if (hour > 12)
+ return invalidDate;
+ if (hour != 12)
+ hour += 12;
+ dateString += 2;
+ while (isspace(*dateString))
+ dateString++;
+ }
+ }
+ } else {
+ dateString = newPosStr;
+ }
+
+ // don't fail if the time zone is missing, some
+ // broken mail-/news-clients omit the time zone
+ if (*dateString) {
+
+ if (strncasecmp(dateString, "GMT", 3) == 0 ||
+ strncasecmp(dateString, "UTC", 3) == 0)
+ {
+ dateString += 3;
+ have_tz = true;
+ }
+
+ while (*dateString && isspace(*dateString))
+ ++dateString;
+
+ if (strncasecmp(dateString, "GMT", 3) == 0) {
+ dateString += 3;
+ }
+ if ((*dateString == '+') || (*dateString == '-')) {
+ offset = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+
+ if ((offset < -9959) || (offset > 9959))
+ return invalidDate;
+
+ int sgn = (offset < 0)? -1:1;
+ offset = abs(offset);
+ if ( *dateString == ':' ) { // GMT+05:00
+ int offset2 = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ offset = (offset*60 + offset2)*sgn;
+ }
+ else
+ offset = ((offset / 100)*60 + (offset % 100))*sgn;
+ have_tz = 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);
+ have_tz = true;
+ break;
+ }
+ }
+ }
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if ( *dateString && year == -1 ) {
+ year = strtol(dateString, &newPosStr, 10);
+ if (errno)
+ return invalidDate;
+ dateString = newPosStr;
+ }
+
+ while (isspace(*dateString))
+ dateString++;
+
+#if 0
+ // Trailing garbage
+ if (*dateString != '\0')
+ return invalidDate;
+#endif
+
+ // Y2K: Solve 2 digit years
+ if ((year >= 0) && (year < 50))
+ year += 2000;
+
+ if ((year >= 50) && (year < 100))
+ year += 1900; // Y2K
+
+ if (!have_tz) {
+ // fall back to midnight, local timezone
+ struct tm t;
+ memset(&t, 0, sizeof(tm));
+ t.tm_mday = day;
+ t.tm_mon = month;
+ t.tm_year = year - 1900;
+ t.tm_isdst = -1;
+ if (have_time) {
+ t.tm_sec = second;
+ t.tm_min = minute;
+ t.tm_hour = hour;
+ }
+
+ // better not use mktime() as it can't handle the full year range
+ return makeTime(&t, 0, false) / 1000.0;
+ }
+
+ result = ymdhms_to_seconds(year, month+1, day, hour, minute, second) - offset*60;
+ return result;
+}
+
+
+double KJS::timeClip(double t)
+{
+ if (isInf(t))
+ return NaN;
+ double at = fabs(t);
+ if (at > 8.64E15)
+ return NaN;
+ return floor(at) * (t != at ? -1 : 1);
+}
+
diff --git a/kjs/date_object.h b/kjs/date_object.h
new file mode 100644
index 000000000..d432b4472
--- /dev/null
+++ b/kjs/date_object.h
@@ -0,0 +1,127 @@
+// -*- 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 _DATE_OBJECT_H_
+#define _DATE_OBJECT_H_
+
+#include "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class DateInstanceImp : public ObjectImp {
+ public:
+ DateInstanceImp(ObjectImp *proto);
+
+ 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 DatePrototypeImp : public DateInstanceImp {
+ public:
+ DatePrototypeImp(ExecState *exec, ObjectPrototypeImp *objectProto);
+ Value get(ExecState *exec, const Identifier &p) const;
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Date.prototype object
+ */
+ class DateProtoFuncImp : public InternalFunctionImp {
+ public:
+ DateProtoFuncImp(ExecState *exec, int i, int len);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+
+ Completion execute(const List &);
+ enum { ToString, ToDateString, ToTimeString, ToLocaleString,
+ ToLocaleDateString, ToLocaleTimeString, ValueOf, GetTime,
+ GetFullYear, GetMonth, GetDate, GetDay, GetHours, GetMinutes,
+ GetSeconds, GetMilliSeconds, GetTimezoneOffset, SetTime,
+ SetMilliSeconds, SetSeconds, SetMinutes, SetHours, SetDate,
+ SetMonth, SetFullYear, ToUTCString,
+ // non-normative properties (Appendix B)
+ GetYear, SetYear, ToGMTString };
+ private:
+ short id;
+ bool utc;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "Date" property
+ */
+ class DateObjectImp : public InternalFunctionImp {
+ public:
+ DateObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ DatePrototypeImp *dateProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ Completion execute(const List &);
+ Object construct(const List &);
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Date object
+ */
+ class DateObjectFuncImp : public InternalFunctionImp {
+ public:
+ DateObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ int i, int len);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { Parse, UTC };
+ private:
+ int id;
+ };
+
+ // helper functions
+ double parseDate(const UString &u);
+ double KRFCDate_parseDate(const UString &_date);
+ double timeClip(double t);
+ double makeTime(struct tm *t, double milli, bool utc);
+
+} // namespace
+
+#endif
diff --git a/kjs/debugger.cpp b/kjs/debugger.cpp
new file mode 100644
index 000000000..4632cc7bf
--- /dev/null
+++ b/kjs/debugger.cpp
@@ -0,0 +1,135 @@
+// -*- 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 "debugger.h"
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "internal.h"
+#include "ustring.h"
+
+using namespace KJS;
+
+// ------------------------------ Debugger -------------------------------------
+
+namespace KJS {
+ struct AttachedInterpreter
+ {
+ public:
+ AttachedInterpreter(Interpreter *i) : interp(i), next(0L) {}
+ Interpreter *interp;
+ AttachedInterpreter *next;
+ };
+
+}
+
+Debugger::Debugger()
+{
+ rep = new DebuggerImp();
+}
+
+Debugger::~Debugger()
+{
+ // detach from all interpreters
+ while (rep->interps)
+ detach(rep->interps->interp);
+
+ delete rep;
+}
+
+void Debugger::attach(Interpreter *interp)
+{
+ if (interp->imp()->debugger() != this)
+ interp->imp()->setDebugger(this);
+
+ // add to the list of attached interpreters
+ if (!rep->interps)
+ rep->interps = new AttachedInterpreter(interp);
+ else {
+ AttachedInterpreter *ai = rep->interps;
+ while (ai->next) {
+ if (ai->interp == interp)
+ return; // already in list
+ ai = ai->next;
+ }
+ ai->next = new AttachedInterpreter(interp);
+ }
+}
+
+void Debugger::detach(Interpreter *interp)
+{
+ if (interp->imp()->debugger() == this)
+ interp->imp()->setDebugger(0L);
+
+ if (!rep->interps)
+ return;
+ // remove from the list of attached interpreters
+ if (rep->interps->interp == interp) {
+ AttachedInterpreter *old = rep->interps;
+ rep->interps = rep->interps->next;
+ delete old;
+ }
+
+ AttachedInterpreter *ai = rep->interps;
+ if (!ai)
+ return;
+ while (ai->next && ai->next->interp != interp)
+ ai = ai->next;
+ if (ai->next) {
+ AttachedInterpreter *old = ai->next;
+ ai->next = ai->next->next;
+ delete old;
+ }
+}
+
+bool Debugger::sourceParsed(ExecState * /*exec*/, int /*sourceId*/,
+ const UString &/*source*/, int /*errorLine*/)
+{
+ return true;
+}
+
+bool Debugger::sourceUnused(ExecState * /*exec*/, int /*sourceId*/)
+{
+ return true;
+}
+
+bool Debugger::exception(ExecState * /*exec*/, const Value &/*value*/,
+ bool /*inTryCatch*/)
+{
+ return true;
+}
+
+bool Debugger::atStatement(ExecState * /*exec*/)
+{
+ return true;
+}
+
+bool Debugger::enterContext(ExecState * /*exec*/)
+{
+ return true;
+}
+
+bool Debugger::exitContext(ExecState * /*exec*/, const Completion &/*completion*/)
+{
+ return true;
+}
diff --git a/kjs/debugger.h b/kjs/debugger.h
new file mode 100644
index 000000000..d30b570e8
--- /dev/null
+++ b/kjs/debugger.h
@@ -0,0 +1,210 @@
+// -*- 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 "interpreter.h"
+
+namespace KJS {
+
+ class DebuggerImp;
+ class Interpreter;
+ class ExecState;
+ class Value;
+ class Object;
+ class UString;
+ class List;
+ class Completion;
+
+ /**
+ * @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 KJS_EXPORT Debugger {
+ public:
+
+ /**
+ * Creates a new debugger
+ */
+ Debugger();
+
+ /**
+ * Destroys the debugger. If the debugger is attached to any interpreters,
+ * it is automatically detached.
+ */
+ virtual ~Debugger();
+
+ DebuggerImp *imp() const { return rep; }
+
+ /**
+ * Attaches the debugger to specified interpreter. This will cause this
+ * object to receive notification of events from the interpreter.
+ *
+ * If the interpreter is deleted, the debugger will automatically be
+ * detached.
+ *
+ * Note: only one debugger can be attached to an interpreter at a time.
+ * Attaching another debugger to the same interpreter will cause the
+ * original debugger to be detached from that interpreter.
+ *
+ * @param interp The interpreter to attach to
+ *
+ * @see detach()
+ */
+ void attach(Interpreter *interp);
+
+ /**
+ * Detach the debugger from an interpreter
+ *
+ * @param interp The interpreter to detach from. If 0, the debugger will be
+ * detached from all interpreters to which it is attached.
+ *
+ * @see attach()
+ */
+ void detach(Interpreter *interp);
+
+ /**
+ * 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 source The source code that was parsed
+ * @param errorLine The line number at which parsing encountered an
+ * error, or -1 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 &source, int errorLine);
+
+ /**
+ * 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 value The value of the exception
+ * @param inTryCatch Whether or not the exception will be caught by the
+ * script
+ * @return true if execution should be continue, false if it should
+ * be aborted
+ */
+ virtual bool exception(ExecState *exec, const Value &value,
+ bool inTryCatch);
+
+ /**
+ * Called when a line of the script is reached (before it is executed)
+ *
+ * The exec pointer's Context object can be inspected to determine
+ * the line number and sourceId of the statement.
+ *
+ * The default implementation does nothing. Override this method if
+ * you want to process this event.
+ *
+ * @param exec The current execution state
+ * @return true if execution should be continue, false if it should
+ * be aborted
+ */
+ virtual bool atStatement(ExecState *exec);
+
+ /**
+ * Called when the interpreter enters a new execution context (stack
+ * frame). This can happen in three situations:
+ *
+ * <ul>
+ * <li>A call to Interpreter::evaluate(). This has a codeType of
+ * GlobalCode, and the sourceId is the id of the code passed to
+ * evaluate(). The lineno here is always 0 since execution starts at the
+ * beginning of the script.</li>
+ * <li>A call to the builtin eval() function. The sourceId corresponds to
+ * the code passed in to eval. This has a codeType of EvalCode. The
+ * lineno here is always 0 since execution starts at the beginning of
+ * the script.</li>
+ * <li>A function call. This only occurs for functions defined in
+ * ECMAScript code, whether via the normal function() { ... } syntax or
+ * a call to the built-in Function() constructor (anonymous functions).
+ * In the former case, the sourceId and lineno indicate the location at
+ * which the function was defined. For anonymous functions, the sourceId
+ * corresponds to the code passed into the Function() constructor.</li>
+ * </ul>
+ *
+ * enterContext() is not called for functions implemented in the native
+ * code, since these do not use an execution context.
+ *
+ * @param exec The current execution state (corresponding to the new stack
+ * frame)
+ */
+ virtual bool enterContext(ExecState *exec);
+
+ /**
+ * Called when the inteprreter exits an execution context. This always
+ * corresponds to a previous call to enterContext()
+ *
+ * @param exec The current execution state (corresponding to the stack frame
+ * being exited from)
+ * @param completion The result of execution of the context. Can be used to
+ * inspect exceptions and return values
+ */
+ virtual bool exitContext(ExecState *exec, const Completion &completion);
+
+ private:
+ DebuggerImp *rep;
+ };
+
+}
+
+#endif
diff --git a/kjs/dtoa.cpp b/kjs/dtoa.cpp
new file mode 100644
index 000000000..a941c1d8a
--- /dev/null
+++ b/kjs/dtoa.cpp
@@ -0,0 +1,3319 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to
+ David M. Gay
+ Bell Laboratories, Room 2C-463
+ 600 Mountain Avenue
+ Murray Hill, NJ 07974-0636
+ U.S.A.
+ dmg@bell-labs.com
+ */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic (D_floating).
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of dtoa.
+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and strtod and dtoa should round accordingly.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and Honor_FLT_ROUNDS is not #defined.
+ * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
+ * that use extended-precision instructions to compute rounded
+ * products and quotients) with IBM.
+ * #define ROUND_BIASED for IEEE-format with biased rounding.
+ * #define Inaccurate_Divide for IEEE-format with correctly rounded
+ * products but inaccurate quotients, e.g., for Intel i860.
+ * #define NO_LONG_LONG on machines that do not have a "long long"
+ * integer type (of >= 64 bits). On such machines, you can
+ * #define Just_16 to store 16 bits per 32-bit Long when doing
+ * high-precision integer arithmetic. Whether this speeds things
+ * up or slows things down depends on the machine and the number
+ * being converted. If long long is available and the name is
+ * something other than "long long", #define Llong to be the name,
+ * and if "unsigned Llong" does not work as an unsigned version of
+ * Llong, #define #ULLong to be the corresponding unsigned type.
+ * #define KR_headers for old-style C function headers.
+ * #define Bad_float_h if your system lacks a float.h or if it does not
+ * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
+ * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
+ * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
+ * if memory is available and otherwise does something you deem
+ * appropriate. If MALLOC is undefined, malloc will be invoked
+ * directly -- and assumed always to succeed.
+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
+ * memory allocations from a private pool of memory when possible.
+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
+ * unless #defined to be a different length. This default length
+ * suffices to get rid of MALLOC calls except for unusual cases,
+ * such as decimal-to-binary conversion of a very long string of
+ * digits. The longest string dtoa can return is about 751 bytes
+ * long. For conversions by strtod of strings of 800 digits and
+ * all dtoa conversions in single-threaded executions with 8-byte
+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
+ * pointers, PRIVATE_MEM >= 7112 appears adequate.
+ * #define INFNAN_CHECK on IEEE systems to cause strtod to check for
+ * Infinity and NaN (case insensitively). On some systems (e.g.,
+ * some HP systems), it may be necessary to #define NAN_WORD0
+ * appropriately -- to the most significant word of a quiet NaN.
+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
+ * strtod also accepts (case insensitively) strings of the form
+ * NaN(x), where x is a string of hexadecimal digits and spaces;
+ * if there is only one string of hexadecimal digits, it is taken
+ * for the 52 fraction bits of the resulting NaN; if there are two
+ * or more strings of hex digits, the first is for the high 20 bits,
+ * the second and subsequent for the low 32 bits, with intervening
+ * white space ignored; but if this results in none of the 52
+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
+ * and NAN_WORD1 are used instead.
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
+ * avoids underflows on inputs whose result does not underflow.
+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
+ * floating-point numbers and flushes underflows to zero rather
+ * than implementing gradual underflow, then you must also #define
+ * Sudden_Underflow.
+ * #define YES_ALIAS to permit aliasing certain double values with
+ * arrays of ULongs. This leads to slightly better code with
+ * some compilers and was always used prior to 19990916, but it
+ * is not strictly legal and can cause trouble with aggressively
+ * optimizing compilers (e.g., gcc 2.95.1 under -O2).
+ * #define USE_LOCALE to use the current locale's decimal_point value.
+ * #define SET_INEXACT if IEEE arithmetic is being used and extra
+ * computation should be done to set the inexact flag when the
+ * result is inexact and avoid setting inexact when the result
+ * is exact. In this case, dtoa.c must be compiled in
+ * an environment, perhaps provided by #include "dtoa.c" in a
+ * suitable wrapper, that defines two functions,
+ * int get_inexact(void);
+ * void clear_inexact(void);
+ * such that get_inexact() returns a nonzero value if the
+ * inexact bit is already set, and clear_inexact() sets the
+ * inexact bit to 0. When SET_INEXACT is #defined, strtod
+ * also does extra computations to set the underflow and overflow
+ * flags when appropriate (i.e., when the result is tiny and
+ * inexact or when it is a numeric value rounded to +-infinity).
+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when
+ * the result overflows to +-Infinity or underflows to 0.
+ */
+
+// Put this before anything else that may import a definition of CONST. CONST from grammar.cpp conflicts with this.
+#ifdef KDE_USE_FINAL
+#undef CONST
+#endif
+
+#include <config.h>
+
+#include "stdlib.h"
+
+#ifdef WORDS_BIGENDIAN
+#define IEEE_MC68k
+#else
+#define IEEE_8087
+#endif
+#define INFNAN_CHECK
+#include "dtoa.h"
+
+
+
+#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 "string.h"
+
+#ifdef USE_LOCALE
+#include "locale.h"
+#endif
+
+#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
+
+#else /* ifndef Bad_float_h */
+#include "float.h"
+#endif /* Bad_float_h */
+
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONST
+#ifdef KR_headers
+#define CONST /* blank */
+#else
+#define CONST const
+#endif
+#endif
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1
+Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.
+#endif
+
+typedef union { double d; ULong L[2]; } U;
+
+#ifdef YES_ALIAS
+#define dval(x) x
+#ifdef IEEE_8087
+#define word0(x) ((ULong *)&x)[1]
+#define word1(x) ((ULong *)&x)[0]
+#else
+#define word0(x) ((ULong *)&x)[0]
+#define word1(x) ((ULong *)&x)[1]
+#endif
+#else
+#ifdef IEEE_8087
+#define word0(x) ((U*)&x)->L[1]
+#define word1(x) ((U*)&x)->L[0]
+#else
+#define word0(x) ((U*)&x)->L[0]
+#define word1(x) ((U*)&x)->L[1]
+#endif
+#define dval(x) ((U*)&x)->d
+#endif
+
+/* The following definition of Storeinc is appropriate for MIPS processors.
+ * An alternative that might be better on some machines is
+ * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
+ */
+#if defined(IEEE_8087) + defined(VAX)
+#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
+((unsigned short *)a)[0] = (unsigned short)c, a++)
+#else
+#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
+((unsigned short *)a)[1] = (unsigned short)c, a++)
+#endif
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax = floor(P*log(2)/log(5)) */
+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#ifdef IEEE_Arith
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Bias 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny0 0
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+#ifndef NO_IEEE_Scale
+#define Avoid_Underflow
+#ifdef Flush_Denorm /* debugging option */
+#undef Sudden_Underflow
+#endif
+#endif
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#define Rounding rounding
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#else /* ifndef IEEE_Arith */
+#undef Check_FLT_ROUNDS
+#undef Honor_FLT_ROUNDS
+#undef SET_INEXACT
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#undef Flt_Rounds
+#define Flt_Rounds 0
+#define Exp_shift 24
+#define Exp_shift1 24
+#define Exp_msk1 0x1000000
+#define Exp_msk11 0x1000000
+#define Exp_mask 0x7f000000
+#define P 14
+#define Bias 65
+#define Exp_1 0x41000000
+#define Exp_11 0x41000000
+#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
+#define Frac_mask 0xffffff
+#define Frac_mask1 0xffffff
+#define Bletch 4
+#define Ten_pmax 22
+#define Bndry_mask 0xefffff
+#define Bndry_mask1 0xffffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 4
+#define Tiny0 0x100000
+#define Tiny1 0
+#define Quick_max 14
+#define Int_max 15
+#else /* VAX */
+#undef Flt_Rounds
+#define Flt_Rounds 1
+#define Exp_shift 23
+#define Exp_shift1 7
+#define Exp_msk1 0x80
+#define Exp_msk11 0x800000
+#define Exp_mask 0x7f80
+#define P 56
+#define Bias 129
+#define Exp_1 0x40800000
+#define Exp_11 0x4080
+#define Ebits 8
+#define Frac_mask 0x7fffff
+#define Frac_mask1 0xffff007f
+#define Ten_pmax 24
+#define Bletch 2
+#define Bndry_mask 0xffff007f
+#define Bndry_mask1 0xffff007f
+#define LSB 0x10000
+#define Sign_bit 0x8000
+#define Log2P 1
+#define Tiny0 0x80
+#define Tiny1 0
+#define Quick_max 15
+#define Int_max 15
+#endif /* IBM, VAX */
+#endif /* IEEE_Arith */
+
+#ifndef IEEE_Arith
+#define ROUND_BIASED
+#endif
+
+#ifdef RND_PRODQUOT
+#define rounded_product(a,b) a = rnd_prod(a, b)
+#define rounded_quotient(a,b) a = rnd_quot(a, b)
+#ifdef KR_headers
+extern double rnd_prod(), rnd_quot();
+#else
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#endif
+#else
+#define rounded_product(a,b) a *= b
+#define rounded_quotient(a,b) a /= b
+#endif
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+
+#ifndef Pack_32
+#define Pack_32
+#endif
+
+#ifdef KR_headers
+#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
+#else
+#define FFFFFFFF 0xffffffffUL
+#endif
+
+#ifdef NO_LONG_LONG
+#undef ULLong
+#ifdef Just_16
+#undef Pack_32
+/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
+ * This makes some inner loops simpler and sometimes saves work
+ * during multiplications, but it often seems to make things slightly
+ * slower. Hence the default is now to store 32 bits per Long.
+ */
+#endif
+#else /* long long available */
+#ifndef Llong
+#define Llong long long
+#endif
+#ifndef ULLong
+#define ULLong unsigned Llong
+#endif
+#endif /* NO_LONG_LONG */
+
+#ifndef MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
+#define FREE_DTOA_LOCK(n) /*nothing*/
+#endif
+
+#define Kmax (sizeof(size_t) << 3)
+
+ struct
+Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ ULong x[1];
+ };
+
+ typedef struct Bigint Bigint;
+
+ static Bigint *freelist[Kmax+1];
+
+ static Bigint *
+Balloc
+#ifdef KR_headers
+ (k) int k;
+#else
+ (int k)
+#endif
+{
+ int x;
+ Bigint *rv;
+#ifndef Omit_Private_Memory
+ unsigned int len;
+#endif
+
+ ACQUIRE_DTOA_LOCK(0);
+ if ((rv = freelist[k])) {
+ freelist[k] = rv->next;
+ }
+ else {
+ x = 1 << k;
+#ifdef Omit_Private_Memory
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
+#else
+ len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
+ /sizeof(double);
+ if (pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint*)pmem_next;
+ pmem_next += len;
+ }
+ else
+ rv = (Bigint*)MALLOC(len*sizeof(double));
+#endif
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+ }
+
+ static void
+Bfree
+#ifdef KR_headers
+ (v) Bigint *v;
+#else
+ (Bigint *v)
+#endif
+{
+ if (v) {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+ }
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(Long) + 2*sizeof(int))
+
+ static Bigint *
+multadd
+#ifdef KR_headers
+ (b, m, a) Bigint *b; int m, a;
+#else
+ (Bigint *b, int m, int a) /* multiply by m and add a */
+#endif
+{
+ int i, wds;
+#ifdef ULLong
+ ULong *x;
+ ULLong carry, y;
+#else
+ ULong carry, *x, y;
+#ifdef Pack_32
+ ULong xi, z;
+#endif
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+#ifdef ULLong
+ y = *x * (ULLong)m + carry;
+ carry = y >> 32;
+ *x++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + carry;
+ carry = y >> 16;
+ *x++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+ }
+
+ static Bigint *
+s2b
+#ifdef KR_headers
+ (s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9;
+#else
+ (CONST char *s, int nd0, int nd, ULong y9)
+#endif
+{
+ Bigint *b;
+ int i, k;
+ Long x, y;
+
+ x = (nd + 8) / 9;
+ for(k = 0, y = 1; x > y; y <<= 1, k++) ;
+#ifdef Pack_32
+ b = Balloc(k);
+ b->x[0] = y9;
+ b->wds = 1;
+#else
+ b = Balloc(k+1);
+ b->x[0] = y9 & 0xffff;
+ b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
+#endif
+
+ i = 9;
+ if (9 < nd0) {
+ s += 9;
+ do b = multadd(b, 10, *s++ - '0');
+ while(++i < nd0);
+ s++;
+ }
+ else
+ s += 10;
+ for(; i < nd; i++)
+ b = multadd(b, 10, *s++ - '0');
+ return b;
+ }
+
+ static int
+hi0bits
+#ifdef KR_headers
+ (x) register ULong x;
+#else
+ (register ULong x)
+#endif
+{
+ register int k = 0;
+
+ if (!(x & 0xffff0000)) {
+ k = 16;
+ x <<= 16;
+ }
+ if (!(x & 0xff000000)) {
+ k += 8;
+ x <<= 8;
+ }
+ if (!(x & 0xf0000000)) {
+ k += 4;
+ x <<= 4;
+ }
+ if (!(x & 0xc0000000)) {
+ k += 2;
+ x <<= 2;
+ }
+ if (!(x & 0x80000000)) {
+ k++;
+ if (!(x & 0x40000000))
+ return 32;
+ }
+ return k;
+ }
+
+ static int
+lo0bits
+#ifdef KR_headers
+ (y) ULong *y;
+#else
+ (ULong *y)
+#endif
+{
+ register int k;
+ register ULong x = *y;
+
+ if (x & 7) {
+ if (x & 1)
+ return 0;
+ if (x & 2) {
+ *y = x >> 1;
+ return 1;
+ }
+ *y = x >> 2;
+ return 2;
+ }
+ k = 0;
+ if (!(x & 0xffff)) {
+ k = 16;
+ x >>= 16;
+ }
+ if (!(x & 0xff)) {
+ k += 8;
+ x >>= 8;
+ }
+ if (!(x & 0xf)) {
+ k += 4;
+ x >>= 4;
+ }
+ if (!(x & 0x3)) {
+ k += 2;
+ x >>= 2;
+ }
+ if (!(x & 1)) {
+ k++;
+ x >>= 1;
+ if (!x & 1)
+ return 32;
+ }
+ *y = x;
+ return k;
+ }
+
+ static Bigint *
+i2b
+#ifdef KR_headers
+ (i) int i;
+#else
+ (int i)
+#endif
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+ }
+
+ static Bigint *
+mult
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+#ifdef ULLong
+ ULLong carry, z;
+#else
+ ULong carry, z;
+#ifdef Pack_32
+ ULong z2;
+#endif
+#endif
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds)
+ k++;
+ c = Balloc(k);
+ for(x = c->x, xa = x + wc; x < xa; x++)
+ *x = 0;
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef ULLong
+ for(; xb < xbe; xc0++) {
+ if ((y = *xb++)) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * (ULLong)y + *xc + carry;
+ carry = z >> 32;
+ *xc++ = z & FFFFFFFF;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#else
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
+ carry = z >> 16;
+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
+ carry = z2 >> 16;
+ Storeinc(xc, z2, z);
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ if (y = *xb >> 16) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ z2 = *xc;
+ do {
+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;
+ carry = z >> 16;
+ Storeinc(xc, z, z2);
+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
+ carry = z2 >> 16;
+ }
+ while(x < xae);
+ *xc = z2;
+ }
+ }
+#else
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * y + *xc + carry;
+ carry = z >> 16;
+ *xc++ = z & 0xffff;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#endif
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+ }
+
+ static Bigint *p5s;
+
+ static Bigint *
+pow5mult
+#ifdef KR_headers
+ (b, k) Bigint *b; int k;
+#else
+ (Bigint *b, int k)
+#endif
+{
+ Bigint *b1, *p5, *p51;
+ int i;
+ static int p05[3] = { 5, 25, 125 };
+
+ if ((i = k & 3))
+ b = multadd(b, p05[i-1], 0);
+
+ if (!(k >>= 2))
+ return b;
+ if (!(p5 = p5s)) {
+ /* first time */
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+#endif
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1))
+ break;
+ if (!(p51 = p5->next)) {
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+#endif
+ }
+ p5 = p51;
+ }
+ return b;
+ }
+
+ static Bigint *
+lshift
+#ifdef KR_headers
+ (b, k) Bigint *b; int k;
+#else
+ (Bigint *b, int k)
+#endif
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *x, *x1, *xe, z;
+
+#ifdef Pack_32
+ n = k >> 5;
+#else
+ n = k >> 4;
+#endif
+ k1 = b->k;
+ n1 = n + b->wds + 1;
+ for(i = b->maxwds; n1 > i; i <<= 1)
+ k1++;
+ b1 = Balloc(k1);
+ x1 = b1->x;
+ for(i = 0; i < n; i++)
+ *x1++ = 0;
+ x = b->x;
+ xe = x + b->wds;
+#ifdef Pack_32
+ if (k &= 0x1f) {
+ k1 = 32 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if ((*x1 = z))
+ ++n1;
+ }
+#else
+ if (k &= 0xf) {
+ k1 = 16 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k & 0xffff | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if (*x1 = z)
+ ++n1;
+ }
+#endif
+ else do
+ *x1++ = *x++;
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+ }
+
+ static int
+cmp
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG
+ if (i > 1 && !a->x[i-1])
+ Bug("cmp called with a->x[a->wds-1] == 0");
+ if (j > 1 && !b->x[j-1])
+ Bug("cmp called with b->x[b->wds-1] == 0");
+#endif
+ if (i -= j)
+ return i;
+ xa0 = a->x;
+ xa = xa0 + j;
+ xb0 = b->x;
+ xb = xb0 + j;
+ for(;;) {
+ if (*--xa != *--xb)
+ return *xa < *xb ? -1 : 1;
+ if (xa <= xa0)
+ break;
+ }
+ return 0;
+ }
+
+ static Bigint *
+diff
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+#ifdef ULLong
+ ULLong borrow, y;
+#else
+ ULong borrow, y;
+#ifdef Pack_32
+ ULong z;
+#endif
+#endif
+
+ i = cmp(a,b);
+ if (!i) {
+ c = Balloc(0);
+ c->wds = 1;
+ c->x[0] = 0;
+ return c;
+ }
+ if (i < 0) {
+ c = a;
+ a = b;
+ b = c;
+ i = 1;
+ }
+ else
+ i = 0;
+ c = Balloc(a->k);
+ c->sign = i;
+ wa = a->wds;
+ xa = a->x;
+ xae = xa + wa;
+ wb = b->wds;
+ xb = b->x;
+ xbe = xb + wb;
+ xc = c->x;
+ borrow = 0;
+#ifdef ULLong
+ do {
+ y = (ULLong)*xa++ - *xb++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+#else
+#ifdef Pack_32
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+#endif
+#endif
+ while(!*--xc)
+ wa--;
+ c->wds = wa;
+ return c;
+ }
+
+ static double
+ulp
+#ifdef KR_headers
+ (x) double x;
+#else
+ (double x)
+#endif
+{
+ register Long L;
+ double a;
+
+ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(a) = L;
+ word1(a) = 0;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ }
+ else {
+ L = -L >> Exp_shift;
+ if (L < Exp_shift) {
+ word0(a) = 0x80000 >> L;
+ word1(a) = 0;
+ }
+ else {
+ word0(a) = 0;
+ L -= Exp_shift;
+ word1(a) = L >= 31 ? 1 : 1 << 31 - L;
+ }
+ }
+#endif
+#endif
+ return dval(a);
+ }
+
+ static double
+b2d
+#ifdef KR_headers
+ (a, e) Bigint *a; int *e;
+#else
+ (Bigint *a, int *e)
+#endif
+{
+ ULong *xa, *xa0, w, y, z;
+ int k;
+ double d;
+#ifdef VAX
+ ULong d0, d1;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG
+ if (!y) Bug("zero y in b2d");
+#endif
+ k = hi0bits(y);
+ *e = 32 - k;
+#ifdef Pack_32
+ if (k < Ebits) {
+ d0 = Exp_1 | y >> Ebits - k;
+ w = xa > xa0 ? *--xa : 0;
+ d1 = y << (32-Ebits) + k | w >> Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ if (k -= Ebits) {
+ d0 = Exp_1 | y << k | z >> 32 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k | y >> 32 - k;
+ }
+ else {
+ d0 = Exp_1 | y;
+ d1 = z;
+ }
+#else
+ if (k < Ebits + 16) {
+ z = xa > xa0 ? *--xa : 0;
+ d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
+ w = xa > xa0 ? *--xa : 0;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ w = xa > xa0 ? *--xa : 0;
+ k -= Ebits + 16;
+ d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = w << k + 16 | y << k;
+#endif
+ ret_d:
+#ifdef VAX
+ word0(d) = d0 >> 16 | d0 << 16;
+ word1(d) = d1 >> 16 | d1 << 16;
+#else
+#undef d0
+#undef d1
+#endif
+ return dval(d);
+ }
+
+ static Bigint *
+d2b
+#ifdef KR_headers
+ (d, e, bits) double d; int *e, *bits;
+#else
+ (double d, int *e, int *bits)
+#endif
+{
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+#ifndef Sudden_Underflow
+ int i;
+#endif
+#ifdef VAX
+ ULong d0, d1;
+ d0 = word0(d) >> 16 | word0(d) << 16;
+ d1 = word1(d) >> 16 | word1(d) << 16;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+#ifdef Pack_32
+ b = Balloc(1);
+#else
+ b = Balloc(2);
+#endif
+ x = b->x;
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+#ifdef Sudden_Underflow
+ de = (int)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if ((de = (int)(d0 >> Exp_shift)))
+ z |= Exp_msk1;
+#endif
+#ifdef Pack_32
+ if ((y = d1)) {
+ if ((k = lo0bits(&y))) {
+ x[0] = y | z << 32 - k;
+ z >>= k;
+ }
+ else
+ x[0] = y;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+#ifdef DEBUG
+ if (!z)
+ Bug("Zero passed to d2b");
+#endif
+ k = lo0bits(&z);
+ x[0] = z;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = 1;
+ k += 32;
+ }
+#else
+ if (y = d1) {
+ if (k = lo0bits(&y))
+ if (k >= 16) {
+ x[0] = y | z << 32 - k & 0xffff;
+ x[1] = z >> k - 16 & 0xffff;
+ x[2] = z >> k;
+ i = 2;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16 | z << 16 - k & 0xffff;
+ x[2] = z >> k & 0xffff;
+ x[3] = z >> k+16;
+ i = 3;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16;
+ x[2] = z & 0xffff;
+ x[3] = z >> 16;
+ i = 3;
+ }
+ }
+ else {
+#ifdef DEBUG
+ if (!z)
+ Bug("Zero passed to d2b");
+#endif
+ k = lo0bits(&z);
+ if (k >= 16) {
+ x[0] = z;
+ i = 0;
+ }
+ else {
+ x[0] = z & 0xffff;
+ x[1] = z >> 16;
+ i = 1;
+ }
+ k += 32;
+ }
+ while(!x[i])
+ --i;
+ b->wds = i + 1;
+#endif
+#ifndef Sudden_Underflow
+ if (de) {
+#endif
+#ifdef IBM
+ *e = (de - Bias - (P-1) << 2) + k;
+ *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
+#else
+ *e = de - Bias - (P-1) + k;
+ *bits = P - k;
+#endif
+#ifndef Sudden_Underflow
+ }
+ else {
+ *e = de - Bias - (P-1) + 1 + k;
+#ifdef Pack_32
+ *bits = 32*i - hi0bits(x[i-1]);
+#else
+ *bits = (i+2)*16 - hi0bits(x[i]);
+#endif
+ }
+#endif
+ return b;
+ }
+#undef d0
+#undef d1
+
+ static double
+ratio
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ double da, db;
+ int k, ka, kb;
+
+ dval(da) = b2d(a, &ka);
+ dval(db) = b2d(b, &kb);
+#ifdef Pack_32
+ k = ka - kb + 32*(a->wds - b->wds);
+#else
+ k = ka - kb + 16*(a->wds - b->wds);
+#endif
+#ifdef IBM
+ if (k > 0) {
+ word0(da) += (k >> 2)*Exp_msk1;
+ if (k &= 3)
+ dval(da) *= 1 << k;
+ }
+ else {
+ k = -k;
+ word0(db) += (k >> 2)*Exp_msk1;
+ if (k &= 3)
+ dval(db) *= 1 << k;
+ }
+#else
+ if (k > 0)
+ word0(da) += k*Exp_msk1;
+ else {
+ k = -k;
+ word0(db) += k*Exp_msk1;
+ }
+#endif
+ return dval(da) / dval(db);
+ }
+
+ static CONST double
+tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+#ifdef VAX
+ , 1e23, 1e24
+#endif
+ };
+
+ static CONST double
+#ifdef IEEE_Arith
+bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,
+#ifdef Avoid_Underflow
+ 9007199254740992.*9007199254740992.e-256
+ /* = 2^106 * 1e-53 */
+#else
+ 1e-256
+#endif
+ };
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#define n_bigtens 5
+#else
+#ifdef IBM
+bigtens[] = { 1e16, 1e32, 1e64 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };
+#define n_bigtens 3
+#else
+bigtens[] = { 1e16, 1e32 };
+static CONST double tinytens[] = { 1e-16, 1e-32 };
+#define n_bigtens 2
+#endif
+#endif
+
+#ifndef IEEE_Arith
+#undef INFNAN_CHECK
+#endif
+
+#ifdef INFNAN_CHECK
+
+#ifndef NAN_WORD0
+#define NAN_WORD0 0x7ff80000
+#endif
+
+#ifndef NAN_WORD1
+#define NAN_WORD1 0
+#endif
+
+ static int
+match
+#ifdef KR_headers
+ (sp, t) char **sp, *t;
+#else
+ (CONST char **sp, CONST char *t)
+#endif
+{
+ int c, d;
+ CONST char *s = *sp;
+
+ while((d = *t++)) {
+ if ((c = *++s) >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != d)
+ return 0;
+ }
+ *sp = s + 1;
+ return 1;
+ }
+
+#ifndef No_Hex_NaN
+ static void
+hexnan
+#ifdef KR_headers
+ (rvp, sp) double *rvp; CONST char **sp;
+#else
+ (double *rvp, CONST char **sp)
+#endif
+{
+ ULong c, x[2];
+ CONST char *s;
+ int havedig, udx0, xshift;
+
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ while((c = *(CONST unsigned char*)++s)) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c += 10 - 'a';
+ else if (c >= 'A' && c <= 'F')
+ c += 10 - 'A';
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ }
+ else if (/*(*/ c == ')' && havedig) {
+ *sp = s + 1;
+ break;
+ }
+ else
+ return; /* invalid form: don't change *sp */
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0)
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ word0(*rvp) = Exp_mask | x[0];
+ word1(*rvp) = x[1];
+ }
+ }
+#endif /*No_Hex_NaN*/
+#endif /* INFNAN_CHECK */
+
+ double
+kjs_strtod
+#ifdef KR_headers
+ (s00, se) CONST char *s00; char **se;
+#else
+ (CONST char *s00, char **se)
+#endif
+{
+#ifdef Avoid_Underflow
+ int scale;
+#endif
+ int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
+ e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
+ CONST char *s, *s0, *s1;
+ double aadj, aadj1, adj, rv, rv0;
+ Long L;
+ ULong y, z;
+ Bigint *bb = NULL, *bb1 = NULL, *bd = NULL, *bd0 = NULL, *bs = NULL, *delta = NULL;
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+#ifdef USE_LOCALE
+ CONST char *s2;
+#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;
+#ifdef USE_LOCALE
+ s1 = localeconv()->decimal_point;
+ if (c == *s1) {
+ c = '.';
+ if (*++s1) {
+ s2 = s;
+ for(;;) {
+ if (*++s2 != *s1) {
+ c = 0;
+ break;
+ }
+ if (!*++s1) {
+ s = s2;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if (c == '.') {
+ c = *++s;
+ if (!nd) {
+ for(; c == '0'; c = *++s)
+ nz++;
+ if (c > '0' && c <= '9') {
+ s0 = s;
+ nf += nz;
+ nz = 0;
+ goto have_dig;
+ }
+ goto dig_done;
+ }
+ for(; c >= '0' && c <= '9'; c = *++s) {
+ have_dig:
+ nz++;
+ if (c -= '0') {
+ nf += nz;
+ for(i = 1; i < nz; i++)
+ if (nd++ < 9)
+ y *= 10;
+ else if (nd <= DBL_DIG + 1)
+ z *= 10;
+ if (nd++ < 9)
+ y = 10*y + c;
+ else if (nd <= DBL_DIG + 1)
+ z = 10*z + c;
+ nz = 0;
+ }
+ }
+ }
+ dig_done:
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ goto ret0;
+ }
+ s00 = s;
+ esign = 0;
+ switch(c = *++s) {
+ case '-':
+ esign = 1;
+ case '+':
+ c = *++s;
+ }
+ if (c >= '0' && c <= '9') {
+ while(c == '0')
+ c = *++s;
+ if (c > '0' && c <= '9') {
+ L = c - '0';
+ s1 = s;
+ while((c = *++s) >= '0' && c <= '9')
+ L = 10*L + c - '0';
+ if (s - s1 > 8 || L > 19999)
+ /* Avoid confusion from exponents
+ * so large that e might overflow.
+ */
+ e = 19999; /* safe for 16 bit ints */
+ else
+ e = (int)L;
+ if (esign)
+ e = -e;
+ }
+ else
+ e = 0;
+ }
+ else
+ s = s00;
+ }
+ if (!nd) {
+ if (!nz && !nz0) {
+#ifdef INFNAN_CHECK
+ /* Check for Nan and Infinity */
+ switch(c) {
+ case 'i':
+ case 'I':
+ if (match(&s,"nf")) {
+ --s;
+ if (!match(&s,"inity"))
+ ++s;
+ word0(rv) = 0x7ff00000;
+ word1(rv) = 0;
+ goto ret;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (match(&s, "an")) {
+ word0(rv) = NAN_WORD0;
+ word1(rv) = NAN_WORD1;
+#ifndef No_Hex_NaN
+ if (*s == '(') /*)*/
+ hexnan(&rv, &s);
+#endif
+ goto ret;
+ }
+ }
+#endif /* INFNAN_CHECK */
+ ret0:
+ s = s00;
+ sign = 0;
+ }
+ goto ret;
+ }
+ e1 = e -= nf;
+
+ /* Now we have nd0 digits, starting at s0, followed by a
+ * decimal point, followed by nd-nd0 digits. The number we're
+ * after is the integer represented by those digits times
+ * 10**e */
+
+ if (!nd0)
+ nd0 = nd;
+ k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
+ dval(rv) = y;
+ if (k > 9) {
+#ifdef SET_INEXACT
+ if (k > DBL_DIG)
+ oldinexact = get_inexact();
+#endif
+ dval(rv) = tens[k - 9] * dval(rv) + z;
+ }
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+#ifndef Honor_FLT_ROUNDS
+ && Flt_Rounds == 1
+#endif
+#endif
+ ) {
+ if (!e)
+ goto ret;
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_product(dval(rv), tens[e]);
+ goto ret;
+#endif
+ }
+ i = DBL_DIG - nd;
+ if (e <= Ten_pmax + i) {
+ /* A fancier test would sometimes let us do
+ * this for larger i values.
+ */
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ e -= i;
+ dval(rv) *= tens[i];
+#ifdef VAX
+ /* VAX exponent range is so narrow we must
+ * worry about overflow here...
+ */
+ vax_ovfl_check:
+ word0(rv) -= P*Exp_msk1;
+ /* rv = */ rounded_product(dval(rv), tens[e]);
+ if ((word0(rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-1-P))
+ goto ovfl;
+ word0(rv) += P*Exp_msk1;
+#else
+ /* rv = */ rounded_product(dval(rv), tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_quotient(dval(rv), tens[-e]);
+ goto ret;
+ }
+#endif
+ }
+ e1 += nd - k;
+
+#ifdef IEEE_Arith
+#ifdef SET_INEXACT
+ inexact = 1;
+ if (k <= DBL_DIG)
+ oldinexact = get_inexact();
+#endif
+#ifdef Avoid_Underflow
+ scale = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding = Flt_Rounds) >= 2) {
+ if (sign)
+ rounding = rounding == 2 ? 0 : 2;
+ else
+ if (rounding != 2)
+ rounding = 0;
+ }
+#endif
+#endif /*IEEE_Arith*/
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if ((i = e1 & 15))
+ dval(rv) *= tens[i];
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ ovfl:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+#ifdef Honor_FLT_ROUNDS
+ switch(rounding) {
+ case 0: /* toward 0 */
+ case 3: /* toward -infinity */
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ break;
+ default:
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+ }
+#else /*Honor_FLT_ROUNDS*/
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+#endif /*Honor_FLT_ROUNDS*/
+#ifdef SET_INEXACT
+ /* set overflow bit */
+ dval(rv0) = 1e300;
+ dval(rv0) *= dval(rv0);
+#endif
+#else /*IEEE_Arith*/
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+#endif /*IEEE_Arith*/
+ if (bd0)
+ goto retfree;
+ goto ret;
+ }
+ e1 >>= 4;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1)
+ dval(rv) *= bigtens[j];
+ /* The last multiplication could overflow. */
+ word0(rv) -= P*Exp_msk1;
+ dval(rv) *= bigtens[j];
+ if ((z = word0(rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-P))
+ goto ovfl;
+ if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ /* set to largest number */
+ /* (Can't trust DBL_MAX) */
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ }
+ else
+ word0(rv) += P*Exp_msk1;
+ }
+ }
+ else if (e1 < 0) {
+ e1 = -e1;
+ if ((i = e1 & 15))
+ dval(rv) /= tens[i];
+ if (e1 >>= 4) {
+ if (e1 >= 1 << n_bigtens)
+ goto undfl;
+#ifdef Avoid_Underflow
+ if (e1 & Scale_Bit)
+ scale = 2*P;
+ for(j = 0; e1 > 0; j++, e1 >>= 1)
+ if (e1 & 1)
+ dval(rv) *= tinytens[j];
+ if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask)
+ >> Exp_shift)) > 0) {
+ /* scaled rv is denormal; zap j low bits */
+ if (j >= 32) {
+ word1(rv) = 0;
+ if (j >= 53)
+ word0(rv) = (P+2)*Exp_msk1;
+ else
+ word0(rv) &= 0xffffffff << j-32;
+ }
+ else
+ word1(rv) &= 0xffffffff << j;
+ }
+#else
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1)
+ dval(rv) *= tinytens[j];
+ /* The last multiplication could underflow. */
+ dval(rv0) = dval(rv);
+ dval(rv) *= tinytens[j];
+ if (!dval(rv)) {
+ dval(rv) = 2.*dval(rv0);
+ dval(rv) *= tinytens[j];
+#endif
+ if (!dval(rv)) {
+ undfl:
+ dval(rv) = 0.;
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ if (bd0)
+ goto retfree;
+ goto ret;
+ }
+#ifndef Avoid_Underflow
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+#endif
+ }
+ }
+
+ /* Now the hard part -- adjusting rv to the correct value.*/
+
+ /* Put digits into bd: true value = bd * 10^e */
+
+ bd0 = s2b(s0, nd0, nd, y);
+
+ for(;;) {
+ bd = Balloc(bd0->k);
+ Bcopy(bd, bd0);
+ bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */
+ bs = i2b(1);
+
+ if (e >= 0) {
+ bb2 = bb5 = 0;
+ bd2 = bd5 = e;
+ }
+ else {
+ bb2 = bb5 = -e;
+ bd2 = bd5 = 0;
+ }
+ if (bbe >= 0)
+ bb2 += bbe;
+ else
+ bd2 -= bbe;
+ bs2 = bb2;
+#ifdef Honor_FLT_ROUNDS
+ if (rounding != 1)
+ bs2++;
+#endif
+#ifdef Avoid_Underflow
+ j = bbe - scale;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) /* denormal */
+ j += P - Emin;
+ else
+ j = P + 1 - bbbits;
+#else /*Avoid_Underflow*/
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else /*Sudden_Underflow*/
+ j = bbe;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) /* denormal */
+ j += P - Emin;
+ else
+ j = P + 1 - bbbits;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ bb2 += j;
+ bd2 += j;
+#ifdef Avoid_Underflow
+ bd2 += scale;
+#endif
+ i = bb2 < bd2 ? bb2 : bd2;
+ if (i > bs2)
+ i = bs2;
+ if (i > 0) {
+ bb2 -= i;
+ bd2 -= i;
+ bs2 -= i;
+ }
+ if (bb5 > 0) {
+ bs = pow5mult(bs, bb5);
+ bb1 = mult(bs, bb);
+ Bfree(bb);
+ bb = bb1;
+ }
+ if (bb2 > 0)
+ bb = lshift(bb, bb2);
+ if (bd5 > 0)
+ bd = pow5mult(bd, bd5);
+ if (bd2 > 0)
+ bd = lshift(bd, bd2);
+ if (bs2 > 0)
+ bs = lshift(bs, bs2);
+ delta = diff(bb, bd);
+ dsign = delta->sign;
+ delta->sign = 0;
+ i = cmp(delta, bs);
+#ifdef Honor_FLT_ROUNDS
+ if (rounding != 1) {
+ if (i < 0) {
+ /* Error is less than an ulp */
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact */
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (rounding) {
+ if (dsign) {
+ adj = 1.;
+ goto apply_adj;
+ }
+ }
+ else if (!dsign) {
+ adj = -1.;
+ if (!word1(rv)
+ && !(word0(rv) & Frac_mask)) {
+ y = word0(rv) & Exp_mask;
+#ifdef Avoid_Underflow
+ if (!scale || y > 2*P*Exp_msk1)
+#else
+ if (y)
+#endif
+ {
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) <= 0)
+ adj = -0.5;
+ }
+ }
+ apply_adj:
+#ifdef Avoid_Underflow
+ if (scale && (y = word0(rv) & Exp_mask)
+ <= 2*P*Exp_msk1)
+ word0(adj) += (2*P+1)*Exp_msk1 - y;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <=
+ P*Exp_msk1) {
+ word0(rv) += P*Exp_msk1;
+ dval(rv) += adj*ulp(dval(rv));
+ word0(rv) -= P*Exp_msk1;
+ }
+ else
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ dval(rv) += adj*ulp(dval(rv));
+ }
+ break;
+ }
+ adj = ratio(delta, bs);
+ if (adj < 1.)
+ adj = 1.;
+ if (adj <= 0x7ffffffe) {
+ /* adj = rounding ? ceil(adj) : floor(adj); */
+ y = adj;
+ if (y != adj) {
+ if (!((rounding>>1) ^ dsign))
+ y++;
+ adj = y;
+ }
+ }
+#ifdef Avoid_Underflow
+ if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1)
+ word0(adj) += (2*P+1)*Exp_msk1 - y;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ word0(rv) += P*Exp_msk1;
+ adj *= ulp(dval(rv));
+ if (dsign)
+ dval(rv) += adj;
+ else
+ dval(rv) -= adj;
+ word0(rv) -= P*Exp_msk1;
+ goto cont;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ adj *= ulp(dval(rv));
+ if (dsign)
+ dval(rv) += adj;
+ else
+ dval(rv) -= adj;
+ goto cont;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+
+ if (i < 0) {
+ /* Error is less than half an ulp -- check for
+ * special case of mantissa a power of two.
+ */
+ if (dsign || word1(rv) || word0(rv) & Bndry_mask
+#ifdef IEEE_Arith
+#ifdef Avoid_Underflow
+ || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1
+#else
+ || (word0(rv) & Exp_mask) <= Exp_msk1
+#endif
+#endif
+ ) {
+#ifdef SET_INEXACT
+ if (!delta->x[0] && delta->wds <= 1)
+ inexact = 0;
+#endif
+ break;
+ }
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact result */
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) > 0)
+ goto drop_down;
+ break;
+ }
+ if (i == 0) {
+ /* exactly half-way between */
+ if (dsign) {
+ if ((word0(rv) & Bndry_mask1) == Bndry_mask1
+ && word1(rv) == (
+#ifdef Avoid_Underflow
+ (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1)
+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
+#endif
+ 0xffffffff)) {
+ /*boundary case -- increment exponent*/
+ word0(rv) = (word0(rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(rv) = 0;
+#ifdef Avoid_Underflow
+ dsign = 0;
+#endif
+ break;
+ }
+ }
+ else if (!(word0(rv) & Bndry_mask) && !word1(rv)) {
+ drop_down:
+ /* boundary case -- decrement exponent */
+#ifdef Sudden_Underflow /*{{*/
+ L = word0(rv) & Exp_mask;
+#ifdef IBM
+ if (L < Exp_msk1)
+#else
+#ifdef Avoid_Underflow
+ if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
+#else
+ if (L <= Exp_msk1)
+#endif /*Avoid_Underflow*/
+#endif /*IBM*/
+ goto undfl;
+ L -= Exp_msk1;
+#else /*Sudden_Underflow}{*/
+#ifdef Avoid_Underflow
+ if (scale) {
+ L = word0(rv) & Exp_mask;
+ if (L <= (2*P+1)*Exp_msk1) {
+ if (L > (P+2)*Exp_msk1)
+ /* round even ==> */
+ /* accept rv */
+ break;
+ /* rv = smallest denormal */
+ goto undfl;
+ }
+ }
+#endif /*Avoid_Underflow*/
+ L = (word0(rv) & Exp_mask) - Exp_msk1;
+#endif /*Sudden_Underflow}}*/
+ word0(rv) = L | Bndry_mask1;
+ word1(rv) = 0xffffffff;
+#ifdef IBM
+ goto cont;
+#else
+ break;
+#endif
+ }
+#ifndef ROUND_BIASED
+ if (!(word1(rv) & LSB))
+ break;
+#endif
+ if (dsign)
+ dval(rv) += ulp(dval(rv));
+#ifndef ROUND_BIASED
+ else {
+ dval(rv) -= ulp(dval(rv));
+#ifndef Sudden_Underflow
+ if (!dval(rv))
+ goto undfl;
+#endif
+ }
+#ifdef Avoid_Underflow
+ dsign = 1 - dsign;
+#endif
+#endif
+ break;
+ }
+ if ((aadj = ratio(delta, bs)) <= 2.) {
+ if (dsign)
+ aadj = aadj1 = 1.;
+ else if (word1(rv) || word0(rv) & Bndry_mask) {
+#ifndef Sudden_Underflow
+ if (word1(rv) == Tiny1 && !word0(rv))
+ goto undfl;
+#endif
+ aadj = 1.;
+ aadj1 = -1.;
+ }
+ else {
+ /* special case -- power of FLT_RADIX to be */
+ /* rounded down... */
+
+ if (aadj < 2./FLT_RADIX)
+ aadj = 1./FLT_RADIX;
+ else
+ aadj *= 0.5;
+ aadj1 = -aadj;
+ }
+ }
+ else {
+ aadj *= 0.5;
+ aadj1 = dsign ? aadj : -aadj;
+#ifdef Check_FLT_ROUNDS
+ switch(Rounding) {
+ case 2: /* towards +infinity */
+ aadj1 -= 0.5;
+ break;
+ case 0: /* towards 0 */
+ case 3: /* towards -infinity */
+ aadj1 += 0.5;
+ }
+#else
+ if (Flt_Rounds == 0)
+ aadj1 += 0.5;
+#endif /*Check_FLT_ROUNDS*/
+ }
+ y = word0(rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ dval(rv0) = dval(rv);
+ word0(rv) -= P*Exp_msk1;
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+ if ((word0(rv) & Exp_mask) >=
+ Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ if (word0(rv0) == Big0 && word1(rv0) == Big1)
+ goto ovfl;
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ goto cont;
+ }
+ else
+ word0(rv) += P*Exp_msk1;
+ }
+ else {
+#ifdef Avoid_Underflow
+ if (scale && y <= 2*P*Exp_msk1) {
+ if (aadj <= 0x7fffffff) {
+ if ((z = (ULong)aadj) <= 0)
+ z = 1;
+ aadj = z;
+ aadj1 = dsign ? aadj : -aadj;
+ }
+ word0(aadj1) += (2*P+1)*Exp_msk1 - y;
+ }
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ dval(rv0) = dval(rv);
+ word0(rv) += P*Exp_msk1;
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#ifdef IBM
+ if ((word0(rv) & Exp_mask) < P*Exp_msk1)
+#else
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1)
+#endif
+ {
+ if (word0(rv0) == Tiny0
+ && word1(rv0) == Tiny1)
+ goto undfl;
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ goto cont;
+ }
+ else
+ word0(rv) -= P*Exp_msk1;
+ }
+ else {
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+ }
+#else /*Sudden_Underflow*/
+ /* Compute adj so that the IEEE rounding rules will
+ * correctly round rv + adj in some half-way cases.
+ * If rv * ulp(rv) is denormalized (i.e.,
+ * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
+ * trouble from bits lost to denormalization;
+ * example: 1.2e-307 .
+ */
+ if (y <= (P-1)*Exp_msk1 && aadj > 1.) {
+ aadj1 = (double)(int)(aadj + 0.5);
+ if (!dsign)
+ aadj1 = -aadj1;
+ }
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ }
+ z = word0(rv) & Exp_mask;
+#ifndef SET_INEXACT
+#ifdef Avoid_Underflow
+ if (!scale)
+#endif
+ if (y == z) {
+ /* Can we stop now? */
+ L = (Long)aadj;
+ aadj -= L;
+ /* The tolerances below are conservative. */
+ if (dsign || word1(rv) || word0(rv) & Bndry_mask) {
+ if (aadj < .4999999 || aadj > .5000001)
+ break;
+ }
+ else if (aadj < .4999999/FLT_RADIX)
+ break;
+ }
+#endif
+ cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+ }
+#ifdef SET_INEXACT
+ if (inexact) {
+ if (!oldinexact) {
+ word0(rv0) = Exp_1 + (70 << Exp_shift);
+ word1(rv0) = 0;
+ dval(rv0) += 1.;
+ }
+ }
+ else if (!oldinexact)
+ clear_inexact();
+#endif
+#ifdef Avoid_Underflow
+ if (scale) {
+ word0(rv0) = Exp_1 - 2*P*Exp_msk1;
+ word1(rv0) = 0;
+ dval(rv) *= dval(rv0);
+#ifndef NO_ERRNO
+ /* try to avoid the bug of testing an 8087 register value */
+ if (word0(rv) == 0 && word1(rv) == 0)
+ errno = ERANGE;
+#endif
+ }
+#endif /* Avoid_Underflow */
+#ifdef SET_INEXACT
+ if (inexact && !(word0(rv) & Exp_mask)) {
+ /* set underflow bit */
+ dval(rv0) = 1e-300;
+ dval(rv0) *= dval(rv0);
+ }
+#endif
+ retfree:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(bd0);
+ Bfree(delta);
+ ret:
+ if (se)
+ *se = (char *)s;
+ return sign ? -dval(rv) : dval(rv);
+ }
+
+ static int
+quorem
+#ifdef KR_headers
+ (b, S) Bigint *b, *S;
+#else
+ (Bigint *b, Bigint *S)
+#endif
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+#ifdef ULLong
+ ULLong borrow, carry, y, ys;
+#else
+ ULong borrow, carry, y, ys;
+#ifdef Pack_32
+ ULong si, z, zs;
+#endif
+#endif
+
+ n = S->wds;
+#ifdef DEBUG
+ /*debug*/ if (b->wds > n)
+ /*debug*/ Bug("oversize b in quorem");
+#endif
+ if (b->wds < n)
+ return 0;
+ sx = S->x;
+ sxe = sx + --n;
+ bx = b->x;
+ bxe = bx + n;
+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
+#ifdef DEBUG
+ /*debug*/ if (q > 9)
+ /*debug*/ Bug("oversized quotient in quorem");
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef ULLong
+ ys = *sx++ * (ULLong)q + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ if (!*bxe) {
+ bx = b->x;
+ while(--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ if (cmp(b, S) >= 0) {
+ q++;
+ borrow = 0;
+ carry = 0;
+ bx = b->x;
+ sx = S->x;
+ do {
+#ifdef ULLong
+ ys = *sx++ + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ return q;
+ }
+
+#ifndef MULTIPLE_THREADS
+ static char *dtoa_result;
+#endif
+
+ static char *
+#ifdef KR_headers
+rv_alloc(i) int i;
+#else
+rv_alloc(int i)
+#endif
+{
+ int j, k, *r;
+
+ j = sizeof(ULong);
+ for(k = 0;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (unsigned)i;
+ j <<= 1)
+ k++;
+ r = (int*)Balloc(k);
+ *r = k;
+ return
+#ifndef MULTIPLE_THREADS
+ dtoa_result =
+#endif
+ (char *)(r+1);
+ }
+
+ static char *
+#ifdef KR_headers
+nrv_alloc(s, rve, n) char *s, **rve; int n;
+#else
+nrv_alloc(CONST char *s, char **rve, int n)
+#endif
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while((*t = *s++)) t++;
+ if (rve)
+ *rve = t;
+ return rv;
+ }
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+ void
+#ifdef KR_headers
+kjs_freedtoa(s) char *s;
+#else
+kjs_freedtoa(char *s)
+#endif
+{
+ Bigint *b = (Bigint *)((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int*)b);
+ Bfree(b);
+#ifndef MULTIPLE_THREADS
+ if (s == dtoa_result)
+ dtoa_result = 0;
+#endif
+ }
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+ char *
+kjs_dtoa
+#ifdef KR_headers
+ (d, mode, ndigits, decpt, sign, rve)
+ double d; int mode, ndigits, *decpt, *sign; char **rve;
+#else
+ (double d, int mode, int ndigits, int *decpt, int *sign, char **rve)
+#endif
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: don't try
+ fast floating-point estimate (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, ilim = 0, ilim0, ilim1 = 0,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+#ifndef Sudden_Underflow
+ int denorm;
+ ULong x;
+#endif
+ Bigint *b, *b1, *delta, *mlo = NULL, *mhi, *S;
+ double d2, ds, eps;
+ char *s, *s0;
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+
+#ifndef MULTIPLE_THREADS
+ if (dtoa_result) {
+ kjs_freedtoa(dtoa_result);
+ dtoa_result = 0;
+ }
+#endif
+
+ if (word0(d) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(d) &= ~Sign_bit; /* clear sign bit */
+ }
+ else
+ *sign = 0;
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(d) & Exp_mask) == Exp_mask)
+#else
+ if (word0(d) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+#ifdef IEEE_Arith
+ if (!word1(d) && !(word0(d) & 0xfffff))
+ return nrv_alloc("Infinity", rve, 8);
+#endif
+ return nrv_alloc("NaN", rve, 3);
+ }
+#endif
+#ifdef IBM
+ dval(d) += 0; /* normalize */
+#endif
+ if (!dval(d)) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+#ifdef SET_INEXACT
+ try_quick = oldinexact = get_inexact();
+ inexact = 1;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding = Flt_Rounds) >= 2) {
+ if (*sign)
+ rounding = rounding == 2 ? 0 : 2;
+ else
+ if (rounding != 2)
+ rounding = 0;
+ }
+#endif
+
+ b = d2b(dval(d), &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {
+#endif
+ dval(d2) = dval(d);
+ word0(d2) &= Frac_mask1;
+ word0(d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(d2) & Frac_mask))
+ dval(d2) /= 1 << j;
+#endif
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+#ifdef IBM
+ i <<= 2;
+ i += j;
+#endif
+#ifndef Sudden_Underflow
+ denorm = 0;
+ }
+ else {
+ /* d is denormalized */
+
+ i = bbits + be + (Bias + (P-1) - 1);
+ x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32
+ : word1(d) << 32 - i;
+ dval(d2) = x;
+ word0(d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+ }
+#endif
+ ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k = (int)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k = floor(ds) */
+ k_check = 1;
+ if (k >= 0 && k <= Ten_pmax) {
+ if (dval(d) < tens[k])
+ k--;
+ k_check = 0;
+ }
+ j = bbits - i - 1;
+ if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+ }
+ else {
+ b2 = -j;
+ s2 = 0;
+ }
+ if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+ }
+ else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode = 0;
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+ try_quick = Rounding == 1;
+#else
+ try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+ if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+ }
+ leftright = 1;
+ switch(mode) {
+ case 0:
+ case 1:
+ ilim = ilim1 = -1;
+ i = 18;
+ ndigits = 0;
+ break;
+ case 2:
+ leftright = 0;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = 0;
+ /* no break */
+ case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0)
+ i = 1;
+ }
+ s = s0 = rv_alloc(i);
+
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1 && rounding != 1)
+ leftright = 0;
+#endif
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ dval(d2) = dval(d);
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ dval(d) /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ dval(d) /= ds;
+ }
+ else if ((j1 = -k)) {
+ dval(d) *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ dval(d) *= bigtens[i];
+ }
+ }
+ if (k_check && dval(d) < 1. && ilim > 0) {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim = ilim1;
+ k--;
+ dval(d) *= 10.;
+ ieps++;
+ }
+ dval(eps) = ieps*dval(d) + 7.;
+ word0(eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ dval(d) -= 5.;
+ if (dval(d) > dval(eps))
+ goto one_digit;
+ if (dval(d) < -dval(eps))
+ goto no_digits;
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ dval(eps) = 0.5/tens[ilim-1] - dval(eps);
+ for(i = 0;;) {
+ L = (long int)dval(d);
+ dval(d) -= L;
+ *s++ = '0' + (int)L;
+ if (dval(d) < dval(eps))
+ goto ret1;
+ if (1. - dval(d) < dval(eps))
+ goto bump_up;
+ if (++i >= ilim)
+ break;
+ dval(eps) *= 10.;
+ dval(d) *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ dval(eps) *= tens[ilim-1];
+ for(i = 1;; i++, dval(d) *= 10.) {
+ L = (Long)(dval(d));
+ if (!(dval(d) -= L))
+ ilim = i;
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (dval(d) > 0.5 + dval(eps))
+ goto bump_up;
+ else if (dval(d) < 0.5 - dval(eps)) {
+ while(*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+ fast_failed:
+ s = s0;
+ dval(d) = dval(d2);
+ k = k0;
+ ilim = ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || dval(d) <= 5*ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for(i = 1;; i++, dval(d) *= 10.) {
+ L = (Long)(dval(d) / ds);
+ dval(d) -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(d) < 0) {
+ L--;
+ dval(d) += ds;
+ }
+#endif
+ *s++ = '0' + (int)L;
+ if (!dval(d)) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (i == ilim) {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+#endif
+ dval(d) += dval(d);
+ if (dval(d) > ds || dval(d) == ds && L & 1) {
+ bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+ }
+
+ m2 = b2;
+ m5 = b5;
+ mhi = mlo = 0;
+ if (leftright) {
+ i =
+#ifndef Sudden_Underflow
+ denorm ? be + (Bias + (P-1) - 1 + 1) :
+#endif
+#ifdef IBM
+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
+#else
+ 1 + P - bbits;
+#endif
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+ }
+ if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+ }
+ if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if ((j = b5 - m5))
+ b = pow5mult(b, j);
+ }
+ else
+ b = pow5mult(b, b5);
+ }
+ S = i2b(1);
+ if (s5 > 0)
+ S = pow5mult(S, s5);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ spec_case = 0;
+ if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && rounding == 1
+#endif
+ ) {
+ if (!word1(d) && !(word0(d) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(d) & (Exp_mask & ~Exp_msk1)
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+ }
+
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+#ifdef Pack_32
+ if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f))
+ i = 32 - i;
+#else
+ if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)
+ i = 16 - i;
+#endif
+ if (i > 4) {
+ i -= 4;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ if (b2 > 0)
+ b = lshift(b, b2);
+ if (s2 > 0)
+ S = lshift(S, s2);
+ if (k_check) {
+ if (cmp(b,S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright)
+ mhi = multadd(mhi, 10, 0);
+ ilim = ilim1;
+ }
+ }
+ if (ilim <= 0 && (mode == 3 || mode == 5)) {
+ if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
+ /* no digits, fcvt style */
+ no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+ one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+ }
+ if (leftright) {
+ if (m2 > 0)
+ mhi = lshift(mhi, m2);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for(i = 1;;i++) {
+ dig = quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+#ifndef ROUND_BIASED
+ if (j1 == 0 && mode != 1 && !(word1(d) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && rounding >= 1
+#endif
+ ) {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+#ifdef SET_INEXACT
+ else if (!b->x[0] && b->wds <= 1)
+ inexact = 0;
+#endif
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if (j < 0 || j == 0 && mode != 1
+#ifndef ROUND_BIASED
+ && !(word1(d) & 1)
+#endif
+ ) {
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if ((j1 > 0 || j1 == 0 && dig & 1)
+ && dig++ == '9')
+ goto round_9_up;
+ }
+ accept_dig:
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+#ifdef Honor_FLT_ROUNDS
+ if (!rounding)
+ goto accept_dig;
+#endif
+ if (dig == '9') { /* possible if i == 1 */
+ round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+ keep_dig:
+#endif
+ *s++ = dig;
+ if (i == ilim)
+ break;
+ b = multadd(b, 10, 0);
+ if (mlo == mhi)
+ mlo = mhi = multadd(mhi, 10, 0);
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+ }
+ else
+ for(i = 1;; i++) {
+ *s++ = dig = quorem(b,S) + '0';
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto ret;
+ }
+ if (i >= ilim)
+ break;
+ b = multadd(b, 10, 0);
+ }
+
+ /* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+ switch(rounding) {
+ case 0: goto trimzeros;
+ case 2: goto roundoff;
+ }
+#endif
+ b = lshift(b, 1);
+ j = cmp(b, S);
+ if (j > 0 || j == 0 && dig & 1) {
+ roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+ }
+ else {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while(*--s == '0');
+ s++;
+ }
+ ret:
+ Bfree(S);
+ if (mhi) {
+ if (mlo && mlo != mhi)
+ Bfree(mlo);
+ Bfree(mhi);
+ }
+ ret1:
+#ifdef SET_INEXACT
+ if (inexact) {
+ if (!oldinexact) {
+ word0(d) = Exp_1 + (70 << Exp_shift);
+ word1(d) = 0;
+ dval(d) += 1.;
+ }
+ }
+ else if (!oldinexact)
+ clear_inexact();
+#endif
+ Bfree(b);
+ *s = 0;
+ *decpt = k + 1;
+ if (rve)
+ *rve = s;
+ return s0;
+ }
+#ifdef __cplusplus
+}
+#endif
diff --git a/kjs/dtoa.h b/kjs/dtoa.h
new file mode 100644
index 000000000..188a4878f
--- /dev/null
+++ b/kjs/dtoa.h
@@ -0,0 +1,31 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_DTOA_H_
+#define _KJS_DTOA_H_
+
+extern "C" double kjs_strtod(const char *s00, char **se);
+extern "C" char *kjs_dtoa(double d, int mode, int ndigits,
+ int *decpt, int *sign, char **rve);
+extern "C" void kjs_freedtoa(char *s);
+
+#endif /* _KJS_DTOA_H */
diff --git a/kjs/error_object.cpp b/kjs/error_object.cpp
new file mode 100644
index 000000000..1b2451489
--- /dev/null
+++ b/kjs/error_object.cpp
@@ -0,0 +1,192 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "error_object.h"
+//#include "debugger.h"
+
+using namespace KJS;
+
+// ------------------------------ ErrorInstanceImp ----------------------------
+
+const ClassInfo ErrorInstanceImp::info = {"Error", 0, 0, 0};
+
+ErrorInstanceImp::ErrorInstanceImp(ObjectImp *proto)
+ : ObjectImp(proto)
+{
+}
+
+// ------------------------------ ErrorPrototypeImp ----------------------------
+
+// ECMA 15.9.4
+ErrorPrototypeImp::ErrorPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto)
+ : ObjectImp(objectProto)
+{
+ Value protect(this);
+ setInternalValue(Undefined());
+ // The constructor will be added later in ErrorObjectImp's constructor
+
+ put(exec, namePropertyName, String("Error"), DontEnum);
+ put(exec, messagePropertyName, String("Unknown error"), DontEnum);
+ putDirect(toStringPropertyName, new ErrorProtoFuncImp(exec,funcProto), DontEnum);
+}
+
+// ------------------------------ ErrorProtoFuncImp ----------------------------
+
+ErrorProtoFuncImp::ErrorProtoFuncImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, NumberImp::zero(), DontDelete|ReadOnly|DontEnum);
+ ident = "toString";
+}
+
+bool ErrorProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value ErrorProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &/*args*/)
+{
+ // toString()
+ UString s = "Error";
+
+ Value v = thisObj.get(exec, namePropertyName);
+ if (v.type() != UndefinedType) {
+ s = v.toString(exec);
+ }
+
+ v = thisObj.get(exec, messagePropertyName);
+ if (v.type() != UndefinedType) {
+ s += ": " + v.toString(exec); // Mozilla compatible format
+ }
+
+ return String(s);
+}
+
+// ------------------------------ ErrorObjectImp -------------------------------
+
+ErrorObjectImp::ErrorObjectImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
+ ErrorPrototypeImp *errorProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ // ECMA 15.11.3.1 Error.prototype
+ putDirect(prototypePropertyName, errorProto, DontEnum|DontDelete|ReadOnly);
+ putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum);
+ //putDirect(namePropertyName, String(n));
+}
+
+bool ErrorObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.9.3
+Object ErrorObjectImp::construct(ExecState *exec, const List &args)
+{
+ Object proto = Object::dynamicCast(exec->lexicalInterpreter()->builtinErrorPrototype());
+ ObjectImp *imp = new ErrorInstanceImp(proto.imp());
+ Object obj(imp);
+
+ if (!args.isEmpty() && args[0].type() != UndefinedType) {
+ imp->putDirect(messagePropertyName, new StringImp(args[0].toString(exec)));
+ }
+
+ return obj;
+}
+
+bool ErrorObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.9.2
+Value ErrorObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ // "Error()" gives the sames result as "new Error()"
+ return construct(exec,args);
+}
+
+// ------------------------------ NativeErrorPrototypeImp ----------------------
+
+NativeErrorPrototypeImp::NativeErrorPrototypeImp(ExecState * /*exec*/, ErrorPrototypeImp *errorProto,
+ ErrorType et, UString name, UString message)
+ : ObjectImp(errorProto)
+{
+ Value protect(this);
+ errType = et;
+ putDirect(namePropertyName, new StringImp(name), 0);
+ putDirect(messagePropertyName, new StringImp(message), 0);
+}
+
+// ------------------------------ NativeErrorImp -------------------------------
+
+const ClassInfo NativeErrorImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
+
+NativeErrorImp::NativeErrorImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
+ const Object &prot)
+ : InternalFunctionImp(funcProto), proto(0)
+{
+ Value protect(this);
+ proto = static_cast<ObjectImp*>(prot.imp());
+
+ putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum); // ECMA 15.11.7.5
+ putDirect(prototypePropertyName, proto, DontDelete|ReadOnly|DontEnum);
+}
+
+bool NativeErrorImp::implementsConstruct() const
+{
+ return true;
+}
+
+Object NativeErrorImp::construct(ExecState *exec, const List &args)
+{
+ ObjectImp *imp = new ErrorInstanceImp(proto);
+ Object obj(imp);
+ if (args[0].type() != UndefinedType)
+ imp->putDirect(messagePropertyName, new StringImp(args[0].toString(exec)));
+ return obj;
+}
+
+bool NativeErrorImp::implementsCall() const
+{
+ return true;
+}
+
+Value NativeErrorImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ return construct(exec,args);
+}
+
+void NativeErrorImp::mark()
+{
+ ObjectImp::mark();
+ if (proto && !proto->marked())
+ proto->mark();
+}
diff --git a/kjs/error_object.h b/kjs/error_object.h
new file mode 100644
index 000000000..21fd84f14
--- /dev/null
+++ b/kjs/error_object.h
@@ -0,0 +1,96 @@
+// -*- 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 _ERROR_OBJECT_H_
+#define _ERROR_OBJECT_H_
+
+#include "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class ErrorInstanceImp : public ObjectImp {
+ public:
+ ErrorInstanceImp(ObjectImp *proto);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ };
+
+ class ErrorPrototypeImp : public ObjectImp {
+ public:
+ ErrorPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto);
+ };
+
+ class ErrorProtoFuncImp : public InternalFunctionImp {
+ public:
+ ErrorProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+ class ErrorObjectImp : public InternalFunctionImp {
+ public:
+ ErrorObjectImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ ErrorPrototypeImp *errorProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+
+
+
+
+ class NativeErrorPrototypeImp : public ObjectImp {
+ public:
+ NativeErrorPrototypeImp(ExecState *exec, ErrorPrototypeImp *errorProto,
+ ErrorType et, UString name, UString message);
+ private:
+ ErrorType errType;
+ };
+
+ class NativeErrorImp : public InternalFunctionImp {
+ public:
+ NativeErrorImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ const Object &prot);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ virtual void mark();
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ private:
+ ObjectImp *proto;
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/function.cpp b/kjs/function.cpp
new file mode 100644
index 000000000..ef5410cf8
--- /dev/null
+++ b/kjs/function.cpp
@@ -0,0 +1,1046 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "function.h"
+
+#include "internal.h"
+#include "function_object.h"
+#include "lexer.h"
+#include "nodes.h"
+#include "operations.h"
+#include "debugger.h"
+#include "context.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+using namespace KJS;
+
+// ------------------------- URI handling functions ---------------------------
+
+// ECMA 15.1.3
+UString encodeURI(ExecState *exec, UString string, UString unescapedSet)
+{
+ char hexdigits[] = "0123456789ABCDEF";
+ int encbufAlloc = 2;
+ UChar *encbuf = (UChar*)malloc(encbufAlloc*sizeof(UChar));
+ int encbufLen = 0;
+
+ for (int k = 0; k < string.size(); k++) {
+
+ UChar C = string[k];
+ if (unescapedSet.find(C) >= 0) {
+ if (encbufLen+1 >= encbufAlloc)
+ encbuf = (UChar*)realloc(encbuf,(encbufAlloc *= 2)*sizeof(UChar));
+ encbuf[encbufLen++] = C;
+ }
+ else {
+ unsigned char octets[4];
+ int octets_len = 0;
+ if (C.uc <= 0x007F) {
+ unsigned short zzzzzzz = C.uc;
+ octets[0] = zzzzzzz;
+ octets_len = 1;
+ }
+ else if (C.uc <= 0x07FF) {
+ unsigned short zzzzzz = C.uc & 0x3F;
+ unsigned short yyyyy = (C.uc >> 6) & 0x1F;
+ octets[0] = 0xC0 | yyyyy;
+ octets[1] = 0x80 | zzzzzz;
+ octets_len = 2;
+ }
+ else if (C.uc >= 0xD800 && C.uc <= 0xDBFF) {
+
+ // we need two chars
+ if (k + 1 >= string.size()) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(encbuf);
+ return UString("");
+ }
+
+ unsigned short Cnext = UChar(string[++k]).uc;
+
+ if (Cnext < 0xDC00 || Cnext > 0xDFFF) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(encbuf);
+ return UString("");
+ }
+
+ unsigned short zzzzzz = Cnext & 0x3F;
+ unsigned short yyyy = (Cnext >> 6) & 0x0F;
+ unsigned short xx = C.uc & 0x03;
+ unsigned short wwww = (C.uc >> 2) & 0x0F;
+ unsigned short vvvv = (C.uc >> 6) & 0x0F;
+ unsigned short uuuuu = vvvv+1;
+ octets[0] = 0xF0 | (uuuuu >> 2);
+ octets[1] = 0x80 | ((uuuuu & 0x03) << 4) | wwww;
+ octets[2] = 0x80 | (xx << 4) | yyyy;
+ octets[3] = 0x80 | zzzzzz;
+ octets_len = 4;
+ }
+ else if (C.uc >= 0xDC00 && C.uc <= 0xDFFF) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(encbuf);
+ return UString("");
+ }
+ else {
+ // 0x0800 - 0xD7FF or 0xE000 - 0xFFFF
+ unsigned short zzzzzz = C.uc & 0x3F;
+ unsigned short yyyyyy = (C.uc >> 6) & 0x3F;
+ unsigned short xxxx = (C.uc >> 12) & 0x0F;
+ octets[0] = 0xE0 | xxxx;
+ octets[1] = 0x80 | yyyyyy;
+ octets[2] = 0x80 | zzzzzz;
+ octets_len = 3;
+ }
+
+ while (encbufLen+3*octets_len >= encbufAlloc)
+ encbuf = (UChar*)realloc(encbuf,(encbufAlloc *= 2)*sizeof(UChar));
+
+ for (int j = 0; j < octets_len; j++) {
+ encbuf[encbufLen++] = '%';
+ encbuf[encbufLen++] = hexdigits[octets[j] >> 4];
+ encbuf[encbufLen++] = hexdigits[octets[j] & 0x0F];
+ }
+ }
+ }
+
+ UString encoded(encbuf,encbufLen);
+ free(encbuf);
+ return encoded;
+}
+
+static bool decodeHex(UChar hi, UChar lo, unsigned short *val)
+{
+ *val = 0;
+ if (hi.uc >= '0' && hi.uc <= '9')
+ *val = (hi.uc-'0') << 4;
+ else if (hi.uc >= 'a' && hi.uc <= 'f')
+ *val = 10+(hi.uc-'a') << 4;
+ else if (hi.uc >= 'A' && hi.uc <= 'F')
+ *val = 10+(hi.uc-'A') << 4;
+ else
+ return false;
+
+ if (lo.uc >= '0' && lo.uc <= '9')
+ *val |= (lo.uc-'0');
+ else if (lo.uc >= 'a' && lo.uc <= 'f')
+ *val |= 10+(lo.uc-'a');
+ else if (lo.uc >= 'A' && lo.uc <= 'F')
+ *val |= 10+(lo.uc-'A');
+ else
+ return false;
+
+ return true;
+}
+
+UString decodeURI(ExecState *exec, UString string, UString reservedSet)
+{
+ int decbufAlloc = 2;
+ UChar *decbuf = (UChar*)malloc(decbufAlloc*sizeof(UChar));
+ int decbufLen = 0;
+
+ for (int k = 0; k < string.size(); k++) {
+ UChar C = string[k];
+
+ if (C != UChar('%')) {
+ // Normal unescaped character
+ if (decbufLen+1 >= decbufAlloc)
+ decbuf = (UChar*)realloc(decbuf,(decbufAlloc *= 2)*sizeof(UChar));
+ decbuf[decbufLen++] = C;
+ continue;
+ }
+
+ // We have % escape sequence... expect at least 2 more characters
+ int start = k;
+ if (k+2 >= string.size()) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(decbuf);
+ return UString("");
+ }
+
+ unsigned short B;
+ if (!decodeHex(string[k+1],string[k+2],&B)) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(decbuf);
+ return UString("");
+ }
+
+ k += 2;
+
+ if (decbufLen+2 >= decbufAlloc)
+ decbuf = (UChar*)realloc(decbuf,(decbufAlloc *= 2)*sizeof(UChar));
+
+ if ((B & 0x80) == 0) {
+ // Single-byte character
+ C = B;
+ }
+ else {
+ // Multi-byte character
+ int n = 0;
+ while (((B << n) & 0x80) != 0)
+ n++;
+
+ if (n < 2 || n > 4) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(decbuf);
+ return UString("");
+ }
+
+ if (k+3*(n-1) >= string.size()) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(decbuf);
+ return UString("");
+ }
+
+ unsigned short octets[4];
+ octets[0] = B;
+ for (int j = 1; j < n; j++) {
+ k++;
+ if ((UChar(string[k]) != UChar('%')) ||
+ !decodeHex(string[k+1],string[k+2],&B) ||
+ ((B & 0xC0) != 0x80)) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(decbuf);
+ return UString("");
+ }
+
+ k += 2;
+ octets[j] = B;
+ }
+
+ // UTF-8 transform
+ const unsigned long replacementChar = 0xFFFD;
+ unsigned long V;
+ if (n == 2) {
+ unsigned long yyyyy = octets[0] & 0x1F;
+ unsigned long zzzzzz = octets[1] & 0x3F;
+ V = (yyyyy << 6) | zzzzzz;
+ // 2-byte sequence overlong for this value?
+ if (V < 0x80)
+ V = replacementChar;
+ C = UChar((unsigned short)V);
+ }
+ else if (n == 3) {
+ unsigned long xxxx = octets[0] & 0x0F;
+ unsigned long yyyyyy = octets[1] & 0x3F;
+ unsigned long zzzzzz = octets[2] & 0x3F;
+ V = (xxxx << 12) | (yyyyyy << 6) | zzzzzz;
+ // 3-byte sequence overlong for this value,
+ // an invalid value or UTF-16 surrogate?
+ if (V < 0x800 || V == 0xFFFE || V == 0xFFFF ||
+ (V >= 0xD800 && V <= 0xDFFF))
+ V = replacementChar;
+ C = UChar((unsigned short)V);
+ }
+ else {
+ assert(n == 4);
+ unsigned long uuuuu = ((octets[0] & 0x07) << 2) | ((octets[1] >> 4) & 0x03);
+ unsigned long vvvv = uuuuu-1;
+ if (vvvv > 0x0F) {
+ Object err = Error::create(exec,URIError);
+ exec->setException(err);
+ free(decbuf);
+ return UString("");
+ }
+ unsigned long wwww = octets[1] & 0x0F;
+ unsigned long xx = (octets[2] >> 4) & 0x03;
+ unsigned long yyyy = octets[2] & 0x0F;
+ unsigned long zzzzzz = octets[3] & 0x3F;
+ unsigned short H = 0xD800 | (vvvv << 6) | (wwww << 2) | xx;
+ unsigned short L = 0xDC00 | (yyyy << 6) | zzzzzz;
+ decbuf[decbufLen++] = UChar(H);
+ decbuf[decbufLen++] = UChar(L);
+ continue;
+ }
+ }
+
+ if (reservedSet.find(C) < 0) {
+ decbuf[decbufLen++] = C;
+ }
+ else {
+ // copy unencoded sequence
+ while (decbufLen+k-start+1 >= decbufAlloc)
+ decbuf = (UChar*)realloc(decbuf,(decbufAlloc *= 2)*sizeof(UChar));
+ for (int p = start; p <= k; p++)
+ decbuf[decbufLen++] = string[p];
+ }
+ }
+
+ UString decoded(decbuf,decbufLen);
+ free(decbuf);
+ return decoded;
+}
+
+static UString uriReserved = ";/?:@&=+$,";
+static UString uriAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static UString DecimalDigit = "0123456789";
+static UString uriMark = "-_.!~*'()";
+static UString uriUnescaped = uriAlpha+DecimalDigit+uriMark;
+
+// ----------------------------- FunctionImp ----------------------------------
+
+const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
+
+namespace KJS {
+ class Parameter {
+ public:
+ Parameter(const Identifier &n) : name(n), next(0L) { }
+ ~Parameter() { delete next; }
+ Identifier name;
+ Parameter *next;
+ };
+}
+
+FunctionImp::FunctionImp(ExecState *exec, const Identifier &n)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
+ ), param(0L), line0(-1), line1(-1), sid(-1)
+{
+ //fprintf(stderr,"FunctionImp::FunctionImp this=%p\n");
+ ident = n;
+}
+
+FunctionImp::~FunctionImp()
+{
+ delete param;
+}
+
+bool FunctionImp::implementsCall() const
+{
+ return true;
+}
+
+Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ Object &globalObj = exec->dynamicInterpreter()->globalObject();
+
+ // enter a new execution context
+ ContextImp ctx(globalObj, exec->dynamicInterpreter()->imp(), thisObj, sid, codeType(),
+ exec->context().imp(), this, &args);
+ ExecState newExec(exec->dynamicInterpreter(), &ctx);
+ newExec.setException(exec->exception()); // could be null
+
+ // assign user supplied arguments to parameters
+ processParameters(&newExec, args);
+ // add variable declarations (initialized to undefined)
+ processVarDecls(&newExec);
+
+ ctx.setLines(line0,line0);
+ Debugger *dbg = exec->interpreter()->imp()->debugger();
+ if (dbg) {
+ if (!dbg->enterContext(&newExec)) {
+ // debugger requested we stop execution
+ dbg->imp()->abort();
+ return Undefined();
+ }
+ }
+
+ Completion comp = execute(&newExec);
+
+ ctx.setLines(line1,line1);
+ if (dbg) {
+ Object func(this);
+ // ### lineno is inaccurate - we really want the end of the function _body_ here
+ // line1 is suppoed to be the end of the function start, just before the body
+ if (!dbg->exitContext(&newExec,comp)) {
+ // debugger requested we stop execution
+ dbg->imp()->abort();
+ return Undefined();
+ }
+ }
+
+ // if an exception occurred, propogate it back to the previous execution object
+ if (newExec.hadException())
+ exec->setException(newExec.exception());
+
+#ifdef KJS_VERBOSE
+ CString n = ident.isEmpty() ? CString("(internal)") : ident.ustring().cstring();
+ if (comp.complType() == Throw) {
+ n += " throws";
+ printInfo(exec, n.c_str(), comp.value());
+ } else if (comp.complType() == ReturnValue) {
+ n += " returns";
+ printInfo(exec, n.c_str(), comp.value());
+ } else
+ fprintf(stderr, "%s returns: undefined\n", n.c_str());
+#endif
+
+ if (comp.complType() == Throw) {
+ exec->setException(comp.value());
+ return comp.value();
+ }
+ else if (comp.complType() == ReturnValue)
+ return comp.value();
+ else
+ return Undefined();
+}
+
+void FunctionImp::addParameter(const Identifier &n)
+{
+ Parameter **p = &param;
+ while (*p)
+ p = &(*p)->next;
+
+ *p = new Parameter(n);
+}
+
+Identifier FunctionImp::parameterProperty(int index) const
+{
+ // Find the property name corresponding to the given parameter
+ int pos = 0;
+ Parameter *p;
+ for (p = param; p && pos < index; p = p->next)
+ pos++;
+
+ if (!p)
+ return Identifier::null();
+
+ // Are there any subsequent parameters with the same name?
+ Identifier name = p->name;
+ for (p = p->next; p; p = p->next)
+ if (p->name == name)
+ return Identifier::null();
+
+ return name;
+}
+
+UString FunctionImp::parameterString() const
+{
+ UString s;
+ const Parameter *p = param;
+ while (p) {
+ if (!s.isEmpty())
+ s += ", ";
+ s += p->name.ustring();
+ p = p->next;
+ }
+
+ return s;
+}
+
+
+// ECMA 10.1.3q
+void FunctionImp::processParameters(ExecState *exec, const List &args)
+{
+ Object variable = exec->context().imp()->variableObject();
+
+#ifdef KJS_VERBOSE
+ fprintf(stderr, "---------------------------------------------------\n"
+ "processing parameters for %s call\n",
+ name().isEmpty() ? "(internal)" : name().ascii());
+#endif
+
+ if (param) {
+ ListIterator it = args.begin();
+ Parameter *p = param;
+ while (p) {
+ if (it != args.end()) {
+#ifdef KJS_VERBOSE
+ fprintf(stderr, "setting parameter %s ", p->name.ascii());
+ printInfo(exec,"to", *it);
+#endif
+ variable.put(exec, p->name, *it);
+ it++;
+ } else
+ variable.put(exec, p->name, Undefined());
+ p = p->next;
+ }
+ }
+#ifdef KJS_VERBOSE
+ else {
+ for (int i = 0; i < args.size(); i++)
+ printInfo(exec,"setting argument", args[i]);
+ }
+#endif
+}
+
+void FunctionImp::processVarDecls(ExecState * /*exec*/)
+{
+}
+
+Value FunctionImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ // Find the arguments from the closest context.
+ if (propertyName == argumentsPropertyName) {
+// delme
+ ContextImp *context = exec->context().imp();
+// fixme
+// ContextImp *context = exec->_context;
+ while (context) {
+ if (context->function() == this)
+ return static_cast<ActivationImp *>
+ (context->activationObject())->get(exec, propertyName);
+ context = context->callingContext();
+ }
+ return Null();
+ }
+
+ // Compute length of parameters.
+ if (propertyName == lengthPropertyName) {
+ const Parameter * p = param;
+ int count = 0;
+ while (p) {
+ ++count;
+ p = p->next;
+ }
+ return Number(count);
+ }
+
+ if (propertyName == callerPropertyName) {
+ ContextImp *context = exec->context().imp();
+ while (context) {
+ if (context->function() == this) {
+ ContextImp *cc = context->callingContext();
+ if (cc && cc->function())
+ return Value(cc->function());
+ else
+ return Null();
+ }
+ context = context->callingContext();
+ }
+ return Null();
+ }
+
+ return InternalFunctionImp::get(exec, propertyName);
+}
+
+void FunctionImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
+{
+ if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
+ return;
+ InternalFunctionImp::put(exec, propertyName, value, attr);
+}
+
+bool FunctionImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
+ return true;
+ return InternalFunctionImp::hasProperty(exec, propertyName);
+}
+
+bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
+{
+ if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
+ return false;
+ return InternalFunctionImp::deleteProperty(exec, propertyName);
+}
+
+// ------------------------------ DeclaredFunctionImp --------------------------
+
+// ### is "Function" correct here?
+const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
+
+DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
+ FunctionBodyNode *b, const ScopeChain &sc)
+ : FunctionImp(exec,n), body(b)
+{
+ Value protect(this);
+ body->ref();
+ setScope(sc);
+ line0 = body->firstLine();
+ line1 = body->lastLine();
+ sid = body->sourceId();
+}
+
+DeclaredFunctionImp::~DeclaredFunctionImp()
+{
+ if ( body->deref() )
+ delete body;
+}
+
+bool DeclaredFunctionImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 13.2.2 [[Construct]]
+Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
+{
+ Object proto;
+ Value p = get(exec,prototypePropertyName);
+ if (p.type() == ObjectType)
+ proto = Object(static_cast<ObjectImp*>(p.imp()));
+ else
+ proto = exec->lexicalInterpreter()->builtinObjectPrototype();
+
+ Object obj(new ObjectImp(proto));
+
+ Value res = call(exec,obj,args);
+
+ if (res.type() == ObjectType)
+ return Object::dynamicCast(res);
+ else
+ return obj;
+}
+
+Completion DeclaredFunctionImp::execute(ExecState *exec)
+{
+ Completion result = body->execute(exec);
+
+ if (result.complType() == Throw || result.complType() == ReturnValue)
+ return result;
+ return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
+}
+
+void DeclaredFunctionImp::processVarDecls(ExecState *exec)
+{
+ body->processVarDecls(exec);
+}
+
+// ------------------------------- ShadowImp -----------------------------------
+
+namespace KJS {
+
+// Acts as a placeholder value to indicate that the actual value is kept
+// in the activation object
+class ShadowImp : public ObjectImp {
+public:
+ ShadowImp(ObjectImp *_obj, Identifier _prop) : obj(_obj), prop(_prop) {}
+ virtual void mark();
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+
+ ObjectImp *obj;
+ Identifier prop;
+};
+
+/*KDE_NOEXPORT*/ const ClassInfo ShadowImp::info = {"Shadow", 0, 0, 0};
+
+void ShadowImp::mark()
+{
+ ObjectImp::mark();
+ if (!obj->marked())
+ obj->mark();
+}
+
+}
+
+// ------------------------------ ArgumentsImp ---------------------------------
+
+const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
+
+// ECMA 10.1.8
+ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args,
+ ActivationImp *act)
+ : ObjectImp(exec->lexicalInterpreter()->builtinObjectPrototype()), activation(act)
+{
+ Value protect(this);
+ putDirect(calleePropertyName, func, DontEnum);
+ putDirect(lengthPropertyName, args.size(), DontEnum);
+ if (!args.isEmpty()) {
+ ListIterator arg = args.begin();
+ for (int i = 0; arg != args.end(); arg++, i++) {
+ Identifier prop = func->parameterProperty(i);
+ if (!prop.isEmpty()) {
+ Object shadow(new ShadowImp(act,prop));
+ ObjectImp::put(exec,Identifier::from(i), shadow, DontEnum);
+ }
+ else {
+ ObjectImp::put(exec,Identifier::from(i), *arg, DontEnum);
+ }
+ }
+ }
+}
+
+void ArgumentsImp::mark()
+{
+ ObjectImp::mark();
+ if (!activation->marked())
+ activation->mark();
+}
+
+Value ArgumentsImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ Value val = ObjectImp::get(exec,propertyName);
+ assert(SimpleNumber::is(val.imp()) || !val.imp()->isDestroyed());
+ Object obj = Object::dynamicCast(val);
+ if (obj.isValid() && obj.inherits(&ShadowImp::info)) {
+ ShadowImp *shadow = static_cast<ShadowImp*>(val.imp());
+ return activation->get(exec,shadow->prop);
+ }
+ else {
+ return val;
+ }
+}
+
+void ArgumentsImp::put(ExecState *exec, const Identifier &propertyName,
+ const Value &value, int attr)
+{
+ Value val = ObjectImp::get(exec,propertyName);
+ Object obj = Object::dynamicCast(val);
+ if (obj.isValid() && obj.inherits(&ShadowImp::info)) {
+ ShadowImp *shadow = static_cast<ShadowImp*>(val.imp());
+ activation->put(exec,shadow->prop,value,attr);
+ }
+ else {
+ ObjectImp::put(exec,propertyName,value,attr);
+ }
+}
+
+// ------------------------------ ActivationImp --------------------------------
+
+const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
+
+// ECMA 10.1.6
+ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
+ : _function(function), _arguments(true), _argumentsObject(0)
+{
+ _arguments = arguments.copy();
+ // FIXME: Do we need to support enumerating the arguments property?
+}
+
+Value ActivationImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == argumentsPropertyName) {
+ // check for locally declared arguments property
+ ValueImp *v = getDirect(propertyName);
+ if (v)
+ return Value(v);
+
+ // default: return builtin arguments array
+ if (!_argumentsObject)
+ _argumentsObject = new ArgumentsImp(exec, _function, _arguments, const_cast<ActivationImp*>(this));
+ return Value(_argumentsObject);
+ }
+ return ObjectImp::get(exec, propertyName);
+}
+
+bool ActivationImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == argumentsPropertyName)
+ return true;
+ return ObjectImp::hasProperty(exec, propertyName);
+}
+
+bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
+{
+ if (propertyName == argumentsPropertyName)
+ return false;
+ return ObjectImp::deleteProperty(exec, propertyName);
+}
+
+void ActivationImp::mark()
+{
+ ObjectImp::mark();
+ if (_function && !_function->marked())
+ _function->mark();
+ _arguments.mark();
+ if (_argumentsObject && !_argumentsObject->marked())
+ _argumentsObject->mark();
+}
+
+// ------------------------------ GlobalFunc -----------------------------------
+
+
+GlobalFuncImp::GlobalFuncImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+ ident = _ident;
+}
+
+CodeType GlobalFuncImp::codeType() const
+{
+ return id == Eval ? EvalCode : codeType();
+}
+
+bool GlobalFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value GlobalFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ Value res;
+
+ static const char do_not_escape[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "*+-./@_";
+
+ switch (id) {
+ case Eval: { // eval()
+ Value x = args[0];
+ if (x.type() != StringType)
+ return x;
+ else {
+ UString s = x.toString(exec);
+
+ int errLine;
+ UString errMsg;
+#ifdef KJS_VERBOSE
+ fprintf(stderr, "eval(): %s\n", s.ascii());
+#endif
+ SourceCode *source;
+ FunctionBodyNode *progNode = Parser::parse(s.data(),s.size(),&source,&errLine,&errMsg);
+
+ // notify debugger that source has been parsed
+ Debugger *dbg = exec->interpreter()->imp()->debugger();
+ if (dbg) {
+ bool cont = dbg->sourceParsed(exec,source->sid,s,errLine);
+ if (!cont) {
+ source->deref();
+ dbg->imp()->abort();
+ if (progNode)
+ delete progNode;
+ return Undefined();
+ }
+ }
+
+ exec->interpreter()->imp()->addSourceCode(source);
+
+ // no program node means a syntax occurred
+ if (!progNode) {
+ Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
+ err.put(exec,"sid",Number(source->sid));
+ exec->setException(err);
+ source->deref();
+ return err;
+ }
+
+ source->deref();
+ progNode->ref();
+
+ // enter a new execution context
+ ContextImp ctx(exec->dynamicInterpreter()->globalObject(),
+ exec->dynamicInterpreter()->imp(),
+ thisObj,
+ source->sid,
+ EvalCode,
+ exec->context().imp());
+
+ ExecState newExec(exec->dynamicInterpreter(), &ctx);
+ newExec.setException(exec->exception()); // could be null
+
+ ctx.setLines(progNode->firstLine(),progNode->firstLine());
+ if (dbg) {
+ if (!dbg->enterContext(&newExec)) {
+ // debugger requested we stop execution
+ dbg->imp()->abort();
+
+ if (progNode->deref())
+ delete progNode;
+ return Undefined();
+ }
+ }
+
+ // execute the code
+ progNode->processVarDecls(&newExec);
+ Completion c = progNode->execute(&newExec);
+
+ res = Undefined();
+
+ ctx.setLines(progNode->lastLine(),progNode->lastLine());
+ if (dbg && !dbg->exitContext(&newExec,c))
+ // debugger requested we stop execution
+ dbg->imp()->abort();
+ else if (newExec.hadException()) // propagate back to parent context
+ exec->setException(newExec.exception());
+ else if (c.complType() == Throw)
+ exec->setException(c.value());
+ else if (c.isValueCompletion())
+ res = c.value();
+
+ if (progNode->deref())
+ delete progNode;
+
+ return res;
+ }
+ break;
+ }
+ case ParseInt: { // ECMA 15.1.2.2
+ CString cstr = args[0].toString(exec).cstring();
+ const char* startptr = cstr.c_str();
+ while ( *startptr && isspace( *startptr ) ) // first, skip leading spaces
+ ++startptr;
+
+ int base = 0;
+ if (args.size() > 1)
+ base = args[1].toInt32(exec);
+
+ double sign = 1;
+ if (*startptr == '-') {
+ sign = -1;
+ startptr++;
+ }
+ else if (*startptr == '+') {
+ sign = 1;
+ startptr++;
+ }
+
+ bool leading0 = false;
+ if ((base == 0 || base == 16) &&
+ (*startptr == '0' && (startptr[1] == 'x' || startptr[1] == 'X'))) {
+ startptr += 2;
+ base = 16;
+ }
+ else if (base == 0 && *startptr == '0') {
+ base = 8;
+ leading0 = true;
+ startptr++;
+ }
+ else if (base == 0) {
+ base = 10;
+ }
+
+ if (base < 2 || base > 36) {
+ res = Number(NaN);
+ }
+ else {
+ long double val = 0;
+ int index = 0;
+ for (; *startptr; startptr++) {
+ int thisval = -1;
+ if (*startptr >= '0' && *startptr <= '9')
+ thisval = *startptr - '0';
+ else if (*startptr >= 'a' && *startptr <= 'z')
+ thisval = 10 + *startptr - 'a';
+ else if (*startptr >= 'A' && *startptr <= 'Z')
+ thisval = 10 + *startptr - 'A';
+
+ if (thisval < 0 || thisval >= base)
+ break;
+
+ val *= base;
+ val += thisval;
+ index++;
+ }
+
+ if (index == 0 && !leading0)
+ res = Number(NaN);
+ else
+ res = Number(double(val)*sign);
+ }
+ break;
+ }
+ case ParseFloat: {
+ UString str = args[0].toString(exec);
+ // don't allow hex numbers here
+ bool isHex = false;
+ if (str.is8Bit()) {
+ const char *c = str.ascii();
+ while (isspace(*c))
+ c++;
+ isHex = (c[0] == '0' && (c[1] == 'x' || c[1] == 'X'));
+ }
+ if (isHex)
+ res = Number(0);
+ else
+ res = Number(str.toDouble( true /*tolerant*/, false ));
+ }
+ break;
+ case IsNaN:
+ res = Boolean(isNaN(args[0].toNumber(exec)));
+ break;
+ case IsFinite: {
+ double n = args[0].toNumber(exec);
+ res = Boolean(!isNaN(n) && !isInf(n));
+ break;
+ }
+ case DecodeURI:
+ res = String(decodeURI(exec,args[0].toString(exec),uriReserved+"#"));
+ break;
+ case DecodeURIComponent:
+ res = String(decodeURI(exec,args[0].toString(exec),""));
+ break;
+ case EncodeURI:
+ res = String(encodeURI(exec,args[0].toString(exec),uriReserved+uriUnescaped+"#"));
+ break;
+ case EncodeURIComponent:
+ res = String(encodeURI(exec,args[0].toString(exec),uriUnescaped));
+ break;
+ case Escape: {
+ 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;
+ }
+ res = String(r);
+ break;
+ }
+ case UnEscape: {
+ 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);
+ }
+ res = String(s);
+ break;
+ }
+ case KJSPrint:
+#ifndef NDEBUG
+ puts(args[0].toString(exec).ascii());
+#endif
+ break;
+ }
+
+ return res;
+}
diff --git a/kjs/function.h b/kjs/function.h
new file mode 100644
index 000000000..777e7d177
--- /dev/null
+++ b/kjs/function.h
@@ -0,0 +1,65 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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_FUNCTION_H_
+#define _KJS_FUNCTION_H_
+
+#include "object.h"
+
+namespace KJS {
+
+ class FunctionPrototypeImp;
+
+ /**
+ * Base class for all function objects.
+ * It implements the hasInstance method (for instanceof, which only applies to function objects)
+ * and allows to give the function a name, used in toString().
+ *
+ * Constructors and prototypes of internal objects (implemented in C++) directly inherit from this.
+ * FunctionImp also does, for functions implemented in JS.
+ */
+ class KJS_EXPORT InternalFunctionImp : public ObjectImp {
+ public:
+ /**
+ * Constructor. For C++-implemented functions, @p funcProto is usually
+ * static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
+ */
+ InternalFunctionImp(FunctionPrototypeImp *funcProto);
+ InternalFunctionImp(ExecState *exec);
+
+ bool implementsHasInstance() const;
+ Boolean hasInstance(ExecState *exec, const Value &value);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ Identifier name() const { return ident; }
+ /// You might want to use the helper function ObjectImp::setFunctionName for this
+ void setName(Identifier _ident) { ident = _ident; }
+
+ protected:
+ Identifier ident;
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/function_object.cpp b/kjs/function_object.cpp
new file mode 100644
index 000000000..83b62aa09
--- /dev/null
+++ b/kjs/function_object.cpp
@@ -0,0 +1,315 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * 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 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 "function_object.h"
+#include "internal.h"
+#include "function.h"
+#include "array_object.h"
+#include "nodes.h"
+#include "lexer.h"
+#include "debugger.h"
+#include "object.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+using namespace KJS;
+
+// ------------------------------ FunctionPrototypeImp -------------------------
+
+FunctionPrototypeImp::FunctionPrototypeImp(ExecState *exec)
+ : InternalFunctionImp((FunctionPrototypeImp*)0)
+{
+ Value protect(this);
+ putDirect(toStringPropertyName,
+ new FunctionProtoFuncImp(exec, this, FunctionProtoFuncImp::ToString, 0, toStringPropertyName),
+ DontEnum);
+ static const Identifier applyPropertyName("apply");
+ putDirect(applyPropertyName,
+ new FunctionProtoFuncImp(exec, this, FunctionProtoFuncImp::Apply, 2, applyPropertyName),
+ DontEnum);
+ static const Identifier callPropertyName("call");
+ putDirect(callPropertyName,
+ new FunctionProtoFuncImp(exec, this, FunctionProtoFuncImp::Call, 1, callPropertyName),
+ DontEnum);
+ putDirect(lengthPropertyName, 0, DontDelete|ReadOnly|DontEnum);
+}
+
+FunctionPrototypeImp::~FunctionPrototypeImp()
+{
+}
+
+bool FunctionPrototypeImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.3.4
+Value FunctionPrototypeImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
+{
+ return Undefined();
+}
+
+// ------------------------------ FunctionProtoFuncImp -------------------------
+
+FunctionProtoFuncImp::FunctionProtoFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+ ident = _ident;
+}
+
+
+bool FunctionProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value FunctionProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ Value result;
+
+ switch (id) {
+ case ToString: {
+ // ### also make this work for internal functions
+ if (!thisObj.isValid() || !thisObj.inherits(&InternalFunctionImp::info)) {
+#ifndef NDEBUG
+ fprintf(stderr,"attempted toString() call on null or non-function object\n");
+#endif
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+ if (thisObj.inherits(&DeclaredFunctionImp::info)) {
+ DeclaredFunctionImp *fi = static_cast<DeclaredFunctionImp*>
+ (thisObj.imp());
+ return String("function " + fi->name().ustring() + "(" +
+ fi->parameterString() + ") " + fi->body->toCode());
+ } else if (thisObj.inherits(&InternalFunctionImp::info) &&
+ !static_cast<InternalFunctionImp*>(thisObj.imp())->name().isNull()) {
+ result = String("\nfunction " + static_cast<InternalFunctionImp*>(thisObj.imp())->name().ustring() + "() {\n"
+ " [native code]\n}\n");
+ }
+ else {
+ result = String("[function]");
+ }
+ }
+ break;
+ case Apply: {
+ Value thisArg = args[0];
+ Value argArray = args[1];
+ Object func = thisObj;
+
+ if (!func.implementsCall()) {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+ Object applyThis;
+ if (thisArg.isA(NullType) || thisArg.isA(UndefinedType))
+ applyThis = exec->dynamicInterpreter()->globalObject();
+ else
+ applyThis = thisArg.toObject(exec);
+
+ List applyArgs;
+ if (!argArray.isA(NullType) && !argArray.isA(UndefinedType)) {
+ if (argArray.isA(ObjectType) &&
+ (Object::dynamicCast(argArray).inherits(&ArrayInstanceImp::info) ||
+ Object::dynamicCast(argArray).inherits(&ArgumentsImp::info))) {
+
+ Object argArrayObj = Object::dynamicCast(argArray);
+ unsigned int length = argArrayObj.get(exec,lengthPropertyName).toUInt32(exec);
+ for (unsigned int i = 0; i < length; i++)
+ applyArgs.append(argArrayObj.get(exec,i));
+ }
+ else {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+ }
+ result = func.call(exec,applyThis,applyArgs);
+ }
+ break;
+ case Call: {
+ Value thisArg = args[0];
+ Object func = thisObj;
+
+ if (!func.implementsCall()) {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+ Object callThis;
+ if (thisArg.isA(NullType) || thisArg.isA(UndefinedType))
+ callThis = exec->dynamicInterpreter()->globalObject();
+ else
+ callThis = thisArg.toObject(exec);
+
+ result = func.call(exec,callThis,args.copyTail());
+ }
+ break;
+ }
+
+ return result;
+}
+
+// ------------------------------ FunctionObjectImp ----------------------------
+
+FunctionObjectImp::FunctionObjectImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ putDirect(prototypePropertyName, funcProto, DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
+}
+
+FunctionObjectImp::~FunctionObjectImp()
+{
+}
+
+bool FunctionObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.3.2 The Function Constructor
+Object FunctionObjectImp::construct(ExecState *exec, const List &args)
+{
+ 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
+ SourceCode *source;
+ int errLine;
+ UString errMsg;
+ FunctionBodyNode *progNode = Parser::parse(body.data(),body.size(),&source,&errLine,&errMsg);
+
+ // notify debugger that source has been parsed
+ Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
+ if (dbg) {
+ bool cont = dbg->sourceParsed(exec,source->sid,body,errLine);
+ if (!cont) {
+ source->deref();
+ dbg->imp()->abort();
+ if (progNode)
+ delete progNode;
+ return Object(new ObjectImp());
+ }
+ }
+
+ exec->interpreter()->imp()->addSourceCode(source);
+
+ // no program node == syntax error - throw a syntax error
+ if (!progNode) {
+ Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
+ // we can't return a Completion(Throw) here, so just set the exception
+ // and return it
+ exec->setException(err);
+ source->deref();
+ return err;
+ }
+ source->deref();
+
+ ScopeChain scopeChain;
+ scopeChain.push(exec->dynamicInterpreter()->globalObject().imp());
+ FunctionBodyNode *bodyNode = progNode;
+
+ FunctionImp *fimp = new DeclaredFunctionImp(exec, Identifier::null(), bodyNode,
+ scopeChain);
+ Object ret(fimp); // protect from GC
+
+ // 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::isIdentLetter(c->uc)) { // else error
+ param = UString(c, 1);
+ c++, i++;
+ while (i < len && (Lexer::isIdentLetter(c->uc) ||
+ Lexer::isDecimalDigit(c->uc))) {
+ param += UString(c, 1);
+ c++, i++;
+ }
+ while (i < len && *c == ' ')
+ c++, i++;
+ if (i == len) {
+ fimp->addParameter(Identifier(param));
+ params++;
+ break;
+ } else if (*c == ',') {
+ fimp->addParameter(Identifier(param));
+ params++;
+ c++, i++;
+ continue;
+ } // else error
+ }
+ Object err = Error::create(exec,SyntaxError,
+ I18N_NOOP("Syntax error in parameter list"),
+ -1);
+ exec->setException(err);
+ return err;
+ }
+
+ List consArgs;
+
+ Object objCons = exec->lexicalInterpreter()->builtinObject();
+ Object prototype = objCons.construct(exec,List::empty());
+ prototype.put(exec, constructorPropertyName, Value(fimp), DontEnum|DontDelete|ReadOnly);
+ fimp->put(exec, prototypePropertyName, prototype, DontEnum|DontDelete|ReadOnly);
+ return ret;
+}
+
+bool FunctionObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.3.1 The Function Constructor Called as a Function
+Value FunctionObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ return construct(exec,args);
+}
+
diff --git a/kjs/function_object.h b/kjs/function_object.h
new file mode 100644
index 000000000..d5b7a281f
--- /dev/null
+++ b/kjs/function_object.h
@@ -0,0 +1,81 @@
+// -*- 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 _FUNCTION_OBJECT_H_
+#define _FUNCTION_OBJECT_H_
+
+#include "internal.h"
+#include "object_object.h"
+#include "function.h"
+
+namespace KJS {
+
+ /**
+ * The initial value of Function.prototype (and thus all objects created
+ * with the Function constructor)
+ */
+ class FunctionPrototypeImp : public InternalFunctionImp {
+ public:
+ FunctionPrototypeImp(ExecState *exec);
+ virtual ~FunctionPrototypeImp();
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Function.prototype object
+ */
+ class FunctionProtoFuncImp : public InternalFunctionImp {
+ public:
+ FunctionProtoFuncImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto, int i, int len, const Identifier &_ident);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { ToString, Apply, Call };
+ private:
+ int id;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "Function" property
+ */
+ class FunctionObjectImp : public InternalFunctionImp {
+ public:
+ FunctionObjectImp(ExecState *exec, FunctionPrototypeImp *funcProto);
+ virtual ~FunctionObjectImp();
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+} // namespace
+
+#endif // _FUNCTION_OBJECT_H_
diff --git a/kjs/global.h.in b/kjs/global.h.in
new file mode 100644
index 000000000..571bc7c5e
--- /dev/null
+++ b/kjs/global.h.in
@@ -0,0 +1,54 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2002 David Faure (david@mandrakesoft.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; 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_GLOBAL_H
+#define KJS_GLOBAL_H
+
+// maximum global call stack size. Protects against accidental or
+// malicious infinite recursions. Define to -1 if you want no limit.
+#define KJS_MAX_STACK 1000
+
+// we don't want any padding between UChars (ARM processor)
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+#define KJS_PACKED __attribute__((__packed__))
+#else
+#define KJS_PACKED
+#endif
+
+#undef __KDE_HAVE_GCC_VISIBILITY
+
+#ifdef __KDE_HAVE_GCC_VISIBILITY
+#define KJS_EXPORT __attribute__ ((visibility("default")))
+#else
+#define KJS_EXPORT
+#endif
+
+#ifndef NDEBUG // protection against problems if committing with KJS_VERBOSE on
+
+// Uncomment this to enable very verbose output from KJS
+//#define KJS_VERBOSE
+// Uncomment this to debug memory allocation and garbage collection
+//#define KJS_DEBUG_MEM
+
+#endif
+
+#endif
diff --git a/kjs/grammar.cpp b/kjs/grammar.cpp
new file mode 100644
index 000000000..03eb07876
--- /dev/null
+++ b/kjs/grammar.cpp
@@ -0,0 +1,3139 @@
+/* A Bison parser, made by GNU Bison 2.1. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names. */
+#define yyparse kjsyyparse
+#define yylex kjsyylex
+#define yyerror kjsyyerror
+#define yylval kjsyylval
+#define yychar kjsyychar
+#define yydebug kjsyydebug
+#define yynerrs kjsyynerrs
+#define yylloc kjsyylloc
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NULLTOKEN = 258,
+ TRUETOKEN = 259,
+ FALSETOKEN = 260,
+ STRING = 261,
+ NUMBER = 262,
+ BREAK = 263,
+ CASE = 264,
+ DEFAULT = 265,
+ FOR = 266,
+ NEW = 267,
+ VAR = 268,
+ CONST = 269,
+ CONTINUE = 270,
+ FUNCTION = 271,
+ RETURN = 272,
+ VOID = 273,
+ DELETE = 274,
+ IF = 275,
+ THIS = 276,
+ DO = 277,
+ WHILE = 278,
+ ELSE = 279,
+ IN = 280,
+ INSTANCEOF = 281,
+ TYPEOF = 282,
+ SWITCH = 283,
+ WITH = 284,
+ RESERVED = 285,
+ THROW = 286,
+ TRY = 287,
+ CATCH = 288,
+ FINALLY = 289,
+ DEBUGGER = 290,
+ EQEQ = 291,
+ NE = 292,
+ STREQ = 293,
+ STRNEQ = 294,
+ LE = 295,
+ GE = 296,
+ OR = 297,
+ AND = 298,
+ PLUSPLUS = 299,
+ MINUSMINUS = 300,
+ LSHIFT = 301,
+ RSHIFT = 302,
+ URSHIFT = 303,
+ PLUSEQUAL = 304,
+ MINUSEQUAL = 305,
+ MULTEQUAL = 306,
+ DIVEQUAL = 307,
+ LSHIFTEQUAL = 308,
+ RSHIFTEQUAL = 309,
+ URSHIFTEQUAL = 310,
+ ANDEQUAL = 311,
+ MODEQUAL = 312,
+ XOREQUAL = 313,
+ OREQUAL = 314,
+ IDENT = 315,
+ FUNCEXPRIDENT = 316,
+ AUTOPLUSPLUS = 317,
+ AUTOMINUSMINUS = 318
+ };
+#endif
+/* Tokens. */
+#define NULLTOKEN 258
+#define TRUETOKEN 259
+#define FALSETOKEN 260
+#define STRING 261
+#define NUMBER 262
+#define BREAK 263
+#define CASE 264
+#define DEFAULT 265
+#define FOR 266
+#define NEW 267
+#define VAR 268
+#define CONST 269
+#define CONTINUE 270
+#define FUNCTION 271
+#define RETURN 272
+#define VOID 273
+#define DELETE 274
+#define IF 275
+#define THIS 276
+#define DO 277
+#define WHILE 278
+#define ELSE 279
+#define IN 280
+#define INSTANCEOF 281
+#define TYPEOF 282
+#define SWITCH 283
+#define WITH 284
+#define RESERVED 285
+#define THROW 286
+#define TRY 287
+#define CATCH 288
+#define FINALLY 289
+#define DEBUGGER 290
+#define EQEQ 291
+#define NE 292
+#define STREQ 293
+#define STRNEQ 294
+#define LE 295
+#define GE 296
+#define OR 297
+#define AND 298
+#define PLUSPLUS 299
+#define MINUSMINUS 300
+#define LSHIFT 301
+#define RSHIFT 302
+#define URSHIFT 303
+#define PLUSEQUAL 304
+#define MINUSEQUAL 305
+#define MULTEQUAL 306
+#define DIVEQUAL 307
+#define LSHIFTEQUAL 308
+#define RSHIFTEQUAL 309
+#define URSHIFTEQUAL 310
+#define ANDEQUAL 311
+#define MODEQUAL 312
+#define XOREQUAL 313
+#define OREQUAL 314
+#define IDENT 315
+#define FUNCEXPRIDENT 316
+#define AUTOPLUSPLUS 317
+#define AUTOMINUSMINUS 318
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "grammar.y"
+
+
+/*
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "nodes.h"
+#include "lexer.h"
+#include "internal.h"
+
+/* default values for bison */
+#define YYDEBUG 0
+#ifdef YYMAXDEPTH
+#undef YYMAXDEPTH
+#endif
+#define YYERROR_VERBOSE
+#define DBG(l, s, e) { l->setLoc(s.first_line, e.last_line, Parser::source); } // location
+
+extern int yylex();
+static int yyerror (const char *);
+static bool automatic();
+
+using namespace KJS;
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 52 "grammar.y"
+typedef union YYSTYPE {
+ int ival;
+ double dval;
+ UString *ustr;
+ Identifier *ident;
+ Node *node;
+ StatementNode *stat;
+ ParameterNode *param;
+ FunctionBodyNode *body;
+ FuncDeclNode *func;
+ FunctionBodyNode *prog;
+ AssignExprNode *init;
+ SourceElementsNode *srcs;
+ StatListNode *slist;
+ ArgumentsNode *args;
+ ArgumentListNode *alist;
+ VarDeclNode *decl;
+ VarDeclListNode *vlist;
+ CaseBlockNode *cblk;
+ ClauseListNode *clist;
+ CaseClauseNode *ccl;
+ ElementNode *elm;
+ Operator op;
+ PropertyValueNode *plist;
+ PropertyNode *pnode;
+ CatchNode *cnode;
+ FinallyNode *fnode;
+} YYSTYPE;
+/* Line 196 of yacc.c. */
+#line 299 "grammar.tab.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined (YYLTYPE) && ! defined (YYLTYPE_IS_DECLARED)
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 219 of yacc.c. */
+#line 323 "grammar.tab.c"
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T) && (defined (__STDC__) || defined (__cplusplus))
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYINCLUDED_STDLIB_H
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2005 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM ((YYSIZE_T) -1)
+# endif
+# ifdef __cplusplus
+extern "C" {
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if (! defined (malloc) && ! defined (YYINCLUDED_STDLIB_H) \
+ && (defined (__STDC__) || defined (__cplusplus)))
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if (! defined (free) && ! defined (YYINCLUDED_STDLIB_H) \
+ && (defined (__STDC__) || defined (__cplusplus)))
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifdef __cplusplus
+}
+# endif
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (defined (YYLTYPE_IS_TRIVIAL) && YYLTYPE_IS_TRIVIAL \
+ && defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short int yyss;
+ YYSTYPE yyvs;
+ YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short int) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined (__GNUC__) && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 196
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 1472
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 88
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 70
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 212
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 384
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 318
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 77, 2, 2, 2, 79, 82, 2,
+ 65, 66, 78, 74, 69, 75, 73, 64, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 72, 87,
+ 80, 86, 81, 85, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 70, 2, 71, 83, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 67, 84, 68, 76, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned short int yyprhs[] =
+{
+ 0, 0, 3, 5, 7, 9, 11, 13, 15, 17,
+ 19, 21, 23, 25, 29, 32, 36, 41, 45, 49,
+ 55, 58, 63, 64, 66, 68, 71, 75, 81, 83,
+ 85, 87, 89, 91, 96, 100, 104, 106, 109, 112,
+ 115, 120, 124, 127, 131, 133, 137, 139, 141, 143,
+ 146, 149, 151, 154, 157, 160, 163, 166, 169, 172,
+ 175, 178, 181, 184, 186, 190, 194, 198, 200, 204,
+ 208, 210, 214, 218, 222, 224, 228, 232, 236, 240,
+ 244, 248, 250, 254, 258, 262, 266, 268, 272, 274,
+ 278, 280, 284, 286, 290, 292, 296, 298, 304, 306,
+ 310, 312, 314, 316, 318, 320, 322, 324, 326, 328,
+ 330, 332, 334, 336, 340, 342, 344, 346, 348, 350,
+ 352, 354, 356, 358, 360, 362, 364, 366, 368, 370,
+ 372, 375, 379, 381, 384, 388, 392, 394, 398, 400,
+ 403, 407, 411, 413, 417, 419, 422, 425, 427, 430,
+ 433, 439, 447, 454, 460, 470, 481, 489, 498, 508,
+ 509, 511, 514, 517, 521, 525, 528, 531, 535, 539,
+ 542, 545, 549, 553, 559, 565, 569, 575, 576, 578,
+ 580, 583, 587, 592, 595, 599, 603, 607, 611, 615,
+ 619, 624, 627, 630, 636, 639, 641, 644, 650, 657,
+ 662, 668, 674, 681, 683, 687, 690, 694, 695, 697,
+ 699, 702, 704
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const short int yyrhs[] =
+{
+ 155, 0, -1, 3, -1, 4, -1, 5, -1, 7,
+ -1, 6, -1, 64, -1, 52, -1, 21, -1, 60,
+ -1, 89, -1, 91, -1, 65, 118, 66, -1, 67,
+ 68, -1, 67, 95, 68, -1, 67, 95, 69, 68,
+ -1, 70, 93, 71, -1, 70, 92, 71, -1, 70,
+ 92, 69, 93, 71, -1, 93, 116, -1, 92, 69,
+ 93, 116, -1, -1, 94, -1, 69, -1, 94, 69,
+ -1, 96, 72, 116, -1, 95, 69, 96, 72, 116,
+ -1, 60, -1, 6, -1, 7, -1, 90, -1, 152,
+ -1, 97, 70, 118, 71, -1, 97, 73, 60, -1,
+ 12, 97, 100, -1, 97, -1, 12, 98, -1, 97,
+ 100, -1, 99, 100, -1, 99, 70, 118, 71, -1,
+ 99, 73, 60, -1, 65, 66, -1, 65, 101, 66,
+ -1, 116, -1, 101, 69, 116, -1, 98, -1, 99,
+ -1, 102, -1, 102, 44, -1, 102, 45, -1, 103,
+ -1, 19, 104, -1, 18, 104, -1, 27, 104, -1,
+ 44, 104, -1, 62, 104, -1, 45, 104, -1, 63,
+ 104, -1, 74, 104, -1, 75, 104, -1, 76, 104,
+ -1, 77, 104, -1, 104, -1, 105, 78, 104, -1,
+ 105, 64, 104, -1, 105, 79, 104, -1, 105, -1,
+ 106, 74, 105, -1, 106, 75, 105, -1, 106, -1,
+ 107, 46, 106, -1, 107, 47, 106, -1, 107, 48,
+ 106, -1, 107, -1, 108, 80, 107, -1, 108, 81,
+ 107, -1, 108, 40, 107, -1, 108, 41, 107, -1,
+ 108, 26, 107, -1, 108, 25, 107, -1, 108, -1,
+ 109, 36, 108, -1, 109, 37, 108, -1, 109, 38,
+ 108, -1, 109, 39, 108, -1, 109, -1, 110, 82,
+ 109, -1, 110, -1, 111, 83, 110, -1, 111, -1,
+ 112, 84, 111, -1, 112, -1, 113, 43, 112, -1,
+ 113, -1, 114, 42, 113, -1, 114, -1, 114, 85,
+ 116, 72, 116, -1, 115, -1, 102, 117, 116, -1,
+ 86, -1, 49, -1, 50, -1, 51, -1, 52, -1,
+ 53, -1, 54, -1, 55, -1, 56, -1, 58, -1,
+ 59, -1, 57, -1, 116, -1, 118, 69, 116, -1,
+ 120, -1, 122, -1, 125, -1, 129, -1, 130, -1,
+ 131, -1, 132, -1, 134, -1, 135, -1, 136, -1,
+ 137, -1, 138, -1, 144, -1, 145, -1, 146, -1,
+ 147, -1, 67, 68, -1, 67, 156, 68, -1, 119,
+ -1, 121, 119, -1, 13, 123, 87, -1, 13, 123,
+ 1, -1, 124, -1, 123, 69, 124, -1, 60, -1,
+ 60, 128, -1, 14, 126, 87, -1, 14, 126, 1,
+ -1, 127, -1, 126, 69, 124, -1, 60, -1, 60,
+ 128, -1, 86, 116, -1, 87, -1, 118, 87, -1,
+ 118, 1, -1, 20, 65, 118, 66, 119, -1, 20,
+ 65, 118, 66, 119, 24, 119, -1, 22, 119, 23,
+ 65, 118, 66, -1, 23, 65, 118, 66, 119, -1,
+ 11, 65, 133, 87, 133, 87, 133, 66, 119, -1,
+ 11, 65, 13, 123, 87, 133, 87, 133, 66, 119,
+ -1, 11, 65, 102, 25, 118, 66, 119, -1, 11,
+ 65, 13, 60, 25, 118, 66, 119, -1, 11, 65,
+ 13, 60, 128, 25, 118, 66, 119, -1, -1, 118,
+ -1, 15, 87, -1, 15, 1, -1, 15, 60, 87,
+ -1, 15, 60, 1, -1, 8, 87, -1, 8, 1,
+ -1, 8, 60, 87, -1, 8, 60, 1, -1, 17,
+ 87, -1, 17, 1, -1, 17, 118, 87, -1, 17,
+ 118, 1, -1, 29, 65, 118, 66, 119, -1, 28,
+ 65, 118, 66, 139, -1, 67, 140, 68, -1, 67,
+ 140, 143, 140, 68, -1, -1, 141, -1, 142, -1,
+ 141, 142, -1, 9, 118, 72, -1, 9, 118, 72,
+ 121, -1, 10, 72, -1, 10, 72, 121, -1, 60,
+ 72, 119, -1, 31, 118, 87, -1, 31, 118, 1,
+ -1, 32, 120, 148, -1, 32, 120, 149, -1, 32,
+ 120, 148, 149, -1, 35, 87, -1, 35, 1, -1,
+ 33, 65, 60, 66, 120, -1, 34, 120, -1, 151,
+ -1, 18, 151, -1, 16, 60, 65, 66, 154, -1,
+ 16, 60, 65, 153, 66, 154, -1, 16, 65, 66,
+ 154, -1, 16, 65, 153, 66, 154, -1, 16, 61,
+ 65, 66, 154, -1, 16, 61, 65, 153, 66, 154,
+ -1, 60, -1, 153, 69, 60, -1, 67, 68, -1,
+ 67, 156, 68, -1, -1, 156, -1, 157, -1, 156,
+ 157, -1, 119, -1, 150, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short int yyrline[] =
+{
+ 0, 169, 169, 170, 171, 172, 173, 174, 177, 184,
+ 185, 186, 187, 188, 189, 190, 191, 195, 196, 197,
+ 201, 202, 207, 208, 212, 213, 217, 218, 223, 224,
+ 225, 229, 230, 231, 232, 233, 237, 238, 242, 243,
+ 244, 245, 249, 250, 254, 255, 259, 260, 264, 265,
+ 266, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 285, 286, 287, 288, 292, 293, 294,
+ 298, 299, 300, 301, 305, 306, 308, 310, 312, 314,
+ 316, 321, 322, 323, 324, 325, 329, 330, 334, 335,
+ 339, 340, 344, 345, 350, 351, 356, 357, 362, 363,
+ 368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
+ 378, 379, 383, 384, 388, 389, 390, 391, 392, 393,
+ 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
+ 407, 408, 412, 413, 417, 419, 429, 430, 435, 436,
+ 440, 442, 452, 453, 458, 459, 463, 467, 471, 473,
+ 481, 482, 487, 488, 489, 492, 495, 498, 501, 507,
+ 508, 512, 513, 517, 518, 525, 526, 530, 531, 539,
+ 540, 544, 545, 553, 558, 563, 564, 569, 570, 574,
+ 575, 579, 580, 584, 585, 589, 594, 595, 602, 603,
+ 604, 608, 609, 618, 623, 627, 629, 633, 634, 639,
+ 641, 643, 645, 650, 651, 655, 657, 662, 665, 670,
+ 671, 675, 676
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "NULLTOKEN", "TRUETOKEN", "FALSETOKEN",
+ "STRING", "NUMBER", "BREAK", "CASE", "DEFAULT", "FOR", "NEW", "VAR",
+ "CONST", "CONTINUE", "FUNCTION", "RETURN", "VOID", "DELETE", "IF",
+ "THIS", "DO", "WHILE", "ELSE", "IN", "INSTANCEOF", "TYPEOF", "SWITCH",
+ "WITH", "RESERVED", "THROW", "TRY", "CATCH", "FINALLY", "DEBUGGER",
+ "EQEQ", "NE", "STREQ", "STRNEQ", "LE", "GE", "OR", "AND", "PLUSPLUS",
+ "MINUSMINUS", "LSHIFT", "RSHIFT", "URSHIFT", "PLUSEQUAL", "MINUSEQUAL",
+ "MULTEQUAL", "DIVEQUAL", "LSHIFTEQUAL", "RSHIFTEQUAL", "URSHIFTEQUAL",
+ "ANDEQUAL", "MODEQUAL", "XOREQUAL", "OREQUAL", "IDENT", "FUNCEXPRIDENT",
+ "AUTOPLUSPLUS", "AUTOMINUSMINUS", "'/'", "'('", "')'", "'{'", "'}'",
+ "','", "'['", "']'", "':'", "'.'", "'+'", "'-'", "'~'", "'!'", "'*'",
+ "'%'", "'<'", "'>'", "'&'", "'^'", "'|'", "'?'", "'='", "';'", "$accept",
+ "Literal", "PrimaryExpr", "ArrayLiteral", "ElementList", "ElisionOpt",
+ "Elision", "PropertyNameAndValueList", "PropertyName", "MemberExpr",
+ "NewExpr", "CallExpr", "Arguments", "ArgumentList", "LeftHandSideExpr",
+ "PostfixExpr", "UnaryExpr", "MultiplicativeExpr", "AdditiveExpr",
+ "ShiftExpr", "RelationalExpr", "EqualityExpr", "BitwiseANDExpr",
+ "BitwiseXORExpr", "BitwiseORExpr", "LogicalANDExpr", "LogicalORExpr",
+ "ConditionalExpr", "AssignmentExpr", "AssignmentOperator", "Expr",
+ "Statement", "Block", "StatementList", "VariableStatement",
+ "VariableDeclarationList", "VariableDeclaration", "ConstStatement",
+ "ConstDeclarationList", "ConstDeclaration", "Initializer",
+ "EmptyStatement", "ExprStatement", "IfStatement", "IterationStatement",
+ "ExprOpt", "ContinueStatement", "BreakStatement", "ReturnStatement",
+ "WithStatement", "SwitchStatement", "CaseBlock", "CaseClausesOpt",
+ "CaseClauses", "CaseClause", "DefaultClause", "LabelledStatement",
+ "ThrowStatement", "TryStatement", "DebuggerStatement", "Catch",
+ "Finally", "FunctionDeclaration", "FunctionDeclarationInternal",
+ "FunctionExpr", "FormalParameterList", "FunctionBody", "Program",
+ "SourceElements", "SourceElement", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short int yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 47, 40, 41, 123, 125, 44,
+ 91, 93, 58, 46, 43, 45, 126, 33, 42, 37,
+ 60, 62, 38, 94, 124, 63, 61, 59
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 88, 89, 89, 89, 89, 89, 89, 89, 90,
+ 90, 90, 90, 90, 90, 90, 90, 91, 91, 91,
+ 92, 92, 93, 93, 94, 94, 95, 95, 96, 96,
+ 96, 97, 97, 97, 97, 97, 98, 98, 99, 99,
+ 99, 99, 100, 100, 101, 101, 102, 102, 103, 103,
+ 103, 104, 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 104, 105, 105, 105, 105, 106, 106, 106,
+ 107, 107, 107, 107, 108, 108, 108, 108, 108, 108,
+ 108, 109, 109, 109, 109, 109, 110, 110, 111, 111,
+ 112, 112, 113, 113, 114, 114, 115, 115, 116, 116,
+ 117, 117, 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 118, 118, 119, 119, 119, 119, 119, 119,
+ 119, 119, 119, 119, 119, 119, 119, 119, 119, 119,
+ 120, 120, 121, 121, 122, 122, 123, 123, 124, 124,
+ 125, 125, 126, 126, 127, 127, 128, 129, 130, 130,
+ 131, 131, 132, 132, 132, 132, 132, 132, 132, 133,
+ 133, 134, 134, 134, 134, 135, 135, 135, 135, 136,
+ 136, 136, 136, 137, 138, 139, 139, 140, 140, 141,
+ 141, 142, 142, 143, 143, 144, 145, 145, 146, 146,
+ 146, 147, 147, 148, 149, 150, 150, 151, 151, 152,
+ 152, 152, 152, 153, 153, 154, 154, 155, 155, 156,
+ 156, 157, 157
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 2, 3, 4, 3, 3, 5,
+ 2, 4, 0, 1, 1, 2, 3, 5, 1, 1,
+ 1, 1, 1, 4, 3, 3, 1, 2, 2, 2,
+ 4, 3, 2, 3, 1, 3, 1, 1, 1, 2,
+ 2, 1, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 1, 3, 3, 3, 1, 3, 3,
+ 1, 3, 3, 3, 1, 3, 3, 3, 3, 3,
+ 3, 1, 3, 3, 3, 3, 1, 3, 1, 3,
+ 1, 3, 1, 3, 1, 3, 1, 5, 1, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 3, 1, 2, 3, 3, 1, 3, 1, 2,
+ 3, 3, 1, 3, 1, 2, 2, 1, 2, 2,
+ 5, 7, 6, 5, 9, 10, 7, 8, 9, 0,
+ 1, 2, 2, 3, 3, 2, 2, 3, 3, 2,
+ 2, 3, 3, 5, 5, 3, 5, 0, 1, 1,
+ 2, 3, 4, 2, 3, 3, 3, 3, 3, 3,
+ 4, 2, 2, 5, 2, 1, 2, 5, 6, 4,
+ 5, 5, 6, 1, 3, 2, 3, 0, 1, 1,
+ 2, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 207, 2, 3, 4, 6, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 10,
+ 0, 0, 7, 0, 0, 22, 0, 0, 0, 0,
+ 147, 11, 31, 12, 36, 46, 47, 48, 51, 63,
+ 67, 70, 74, 81, 86, 88, 90, 92, 94, 96,
+ 98, 112, 0, 211, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 212, 195, 32, 0, 208, 209, 166, 0, 165, 159,
+ 0, 10, 0, 36, 37, 138, 0, 136, 144, 0,
+ 142, 162, 0, 161, 0, 0, 0, 170, 0, 169,
+ 0, 48, 53, 196, 52, 0, 0, 0, 54, 0,
+ 0, 0, 0, 0, 192, 191, 55, 57, 0, 56,
+ 58, 0, 6, 5, 10, 14, 0, 0, 0, 24,
+ 0, 0, 23, 59, 60, 61, 62, 0, 0, 0,
+ 38, 0, 0, 39, 49, 50, 101, 102, 103, 104,
+ 105, 106, 107, 108, 111, 109, 110, 100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 149, 0, 148, 1, 210, 168, 167,
+ 0, 48, 160, 0, 29, 30, 28, 14, 35, 0,
+ 139, 135, 0, 134, 145, 141, 0, 140, 164, 163,
+ 0, 0, 203, 0, 0, 172, 171, 0, 0, 0,
+ 0, 0, 187, 186, 130, 0, 0, 188, 189, 185,
+ 13, 15, 0, 0, 131, 22, 18, 17, 20, 25,
+ 42, 0, 44, 0, 34, 0, 41, 99, 65, 64,
+ 66, 68, 69, 71, 72, 73, 80, 79, 77, 78,
+ 75, 76, 82, 83, 84, 85, 87, 89, 91, 93,
+ 95, 0, 113, 138, 0, 0, 159, 146, 137, 143,
+ 0, 0, 0, 0, 0, 199, 0, 0, 0, 0,
+ 0, 0, 0, 0, 194, 190, 16, 0, 26, 0,
+ 43, 0, 33, 40, 0, 0, 139, 159, 0, 0,
+ 197, 0, 201, 0, 205, 0, 200, 204, 150, 0,
+ 153, 177, 174, 173, 0, 0, 19, 21, 45, 97,
+ 0, 0, 0, 0, 159, 198, 202, 206, 0, 152,
+ 0, 0, 178, 179, 0, 27, 0, 0, 159, 156,
+ 0, 151, 0, 0, 175, 177, 180, 193, 157, 0,
+ 0, 0, 181, 183, 0, 158, 0, 154, 132, 182,
+ 184, 176, 155, 133
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short int yydefgoto[] =
+{
+ -1, 41, 42, 43, 140, 141, 142, 136, 137, 44,
+ 45, 46, 150, 251, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 168,
+ 62, 63, 64, 379, 65, 96, 97, 66, 99, 100,
+ 210, 67, 68, 69, 70, 203, 71, 72, 73, 74,
+ 75, 332, 351, 352, 353, 365, 76, 77, 78, 79,
+ 237, 238, 80, 81, 82, 224, 295, 83, 138, 85
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -271
+static const short int yypact[] =
+{
+ 824, -271, -271, -271, -271, -271, 5, -12, 118, 15,
+ 45, 8, -6, 362, 1320, 1395, 81, -271, 901, 92,
+ 1395, 96, 104, 1395, 106, 13, 1395, 1395, -271, -14,
+ 1395, 1395, -271, 1395, 439, 111, 1395, 1395, 1395, 1395,
+ -271, -271, -271, -271, -25, -271, 71, 197, -271, -271,
+ -27, 63, 227, 91, 129, 194, 103, 130, 166, -15,
+ -271, -271, 9, -271, -271, -271, -271, -271, -271, -271,
+ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
+ -271, -271, -271, 279, 824, -271, -271, 27, -271, 1020,
+ -19, -271, 29, -25, -271, 208, 11, -271, 208, 17,
+ -271, -271, 32, -271, 216, 230, -17, -271, 1395, -271,
+ 21, 195, -271, -271, -271, 1395, 274, 1395, -271, 1395,
+ 1395, 33, 516, 252, -271, -271, -271, -271, 901, -271,
+ -271, 37, 226, 228, -14, 986, 219, 229, 593, -271,
+ 57, 1095, 233, -271, -271, -271, -271, 1170, 1395, 243,
+ -271, 1395, 244, -271, -271, -271, -271, -271, -271, -271,
+ -271, -271, -271, -271, -271, -271, -271, -271, 1395, 1395,
+ 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395,
+ 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395,
+ 1395, 1395, 1395, -271, 1395, -271, -271, -271, -271, -271,
+ 245, 213, 237, 221, -271, -271, -271, -271, -271, 1395,
+ -271, -271, 15, -271, -271, -271, 15, -271, -271, -271,
+ -10, 3, -271, 246, 49, -271, -271, 76, 247, 90,
+ 121, 125, -271, -271, -271, 249, 106, 276, -271, -271,
+ -271, -271, 87, 1395, -271, 111, -271, -271, -271, -271,
+ -271, 126, -271, 141, -271, 174, -271, -271, -271, -271,
+ -271, -27, -27, 63, 63, 63, 227, 227, 227, 227,
+ 227, 227, 91, 91, 91, 91, 129, 194, 103, 130,
+ 166, 239, -271, 1, -48, 1395, 1395, -271, -271, -271,
+ 246, 127, 246, 131, 670, -271, 246, 255, 901, 1395,
+ 901, 250, 901, 256, -271, -271, -271, 248, -271, 1245,
+ -271, 1395, -271, -271, 1395, 1395, 293, 1395, 132, 232,
+ -271, 246, -271, 246, -271, 747, -271, -271, 297, 133,
+ -271, 313, -271, -271, 257, 1395, -271, -271, -271, -271,
+ 137, 1395, 240, 901, 1395, -271, -271, -271, 901, -271,
+ 1395, 31, 313, -271, 106, -271, 901, 138, 1395, -271,
+ 258, -271, 139, 254, -271, 313, -271, -271, -271, 901,
+ 262, 901, 901, 901, 261, -271, 901, -271, -271, 901,
+ 901, -271, -271, -271
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const short int yypgoto[] =
+{
+ -271, -271, -271, -271, -271, 89, -271, -271, 93, 324,
+ 329, -271, -2, -271, 46, -271, -7, 117, 85, -29,
+ -9, 152, 153, 151, 154, 155, -271, -271, -130, -271,
+ -8, -18, -23, -30, -271, 145, -145, -271, -271, -271,
+ -94, -271, -271, -271, -271, -270, -271, -271, -271, -271,
+ -271, -271, -16, -271, -5, -271, -271, -271, -271, -271,
+ -271, 115, -271, 334, -271, 72, -163, -271, 2, -81
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -131
+static const short int yytable[] =
+{
+ 116, 123, 84, 197, 214, 110, 86, 112, 114, 101,
+ 193, 248, 211, 118, 124, 121, 319, 252, 215, 126,
+ 127, 212, 225, 129, 130, 131, 315, 191, 198, 143,
+ 144, 145, 146, 218, 232, 204, 205, 169, 257, 317,
+ 147, 363, 105, 222, 153, 148, 106, 342, 149, 223,
+ 222, 170, 171, 89, 104, 105, 290, 197, 128, 106,
+ 111, 111, 281, 222, 282, 87, 111, 288, 102, 292,
+ 192, 289, 111, 111, 360, 95, 111, 111, 194, 287,
+ 212, 202, 111, 111, 111, 111, 216, 209, 370, 206,
+ 194, 208, 88, 204, 205, 103, 195, 207, 213, 364,
+ 125, 112, 194, 240, 217, 98, 194, 227, 226, 229,
+ 239, 230, 231, 308, 199, 296, 177, 178, 297, 219,
+ 233, 1, 2, 3, 4, 5, 245, 320, 246, 322,
+ 8, 179, 180, 326, 90, 201, 147, 172, 173, 17,
+ 253, 151, 298, 255, 152, 194, 115, 206, 266, 267,
+ 268, 269, 270, 271, 111, 306, 300, 117, 345, 194,
+ 346, 119, 258, 259, 260, 183, 184, 185, 186, 120,
+ 28, 181, 182, 122, 272, 273, 274, 275, 91, 337,
+ 139, 338, 32, 33, 339, 92, 188, 301, 35, 316,
+ 194, 302, 310, 321, 194, 311, 297, 323, 343, 349,
+ 297, 194, 194, 356, 369, 355, 194, 194, 194, 190,
+ 194, 372, 312, 304, 189, 111, 111, 111, 111, 111,
+ 111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
+ 111, 111, 111, 111, 111, 111, 111, 111, 285, 154,
+ 155, 154, 155, 194, 197, 313, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 154, 155, 263,
+ 264, 265, 156, 157, 158, 159, 160, 161, 162, 163,
+ 164, 165, 166, 174, 175, 176, 187, 318, 202, 196,
+ 328, 220, 330, 167, 333, 235, 236, 241, 242, 261,
+ 262, 329, 291, 293, 209, 221, 325, 228, -29, 167,
+ -30, 243, 249, 254, 256, 283, 194, 340, 286, 202,
+ 236, 314, 299, 294, 303, 327, 334, 331, 341, 344,
+ 335, 348, 350, 354, 371, 359, 373, 358, 376, 381,
+ 361, 367, 93, 357, 309, 307, 202, 94, 368, 276,
+ 278, 277, 362, 380, 279, 284, 280, 366, 113, 374,
+ 202, 375, 305, 377, 378, 378, 0, 0, 382, 0,
+ 0, 383, 383, 107, 0, 1, 2, 3, 4, 5,
+ 0, 0, 0, 0, 8, 0, 0, 0, 90, 0,
+ 108, 15, 0, 17, 0, 0, 0, 0, 0, 20,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 26, 27, 0, 0,
+ 0, 0, 0, 0, 28, 0, 0, 0, 0, 0,
+ 0, 0, 91, 0, 30, 31, 32, 33, 0, 92,
+ 0, 0, 35, 0, 0, 0, 36, 37, 38, 39,
+ 0, 0, 1, 2, 3, 132, 133, 6, 0, 109,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 0, 0, 0, 20, 21, 22, 0,
+ 23, 24, 0, 0, 25, 0, 0, 0, 0, 0,
+ 0, 0, 0, 26, 27, 0, 0, 0, 0, 0,
+ 0, 28, 0, 0, 0, 0, 0, 0, 0, 134,
+ 0, 30, 31, 32, 33, 0, 34, 135, 0, 35,
+ 0, 0, 0, 36, 37, 38, 39, 0, 0, 1,
+ 2, 3, 4, 5, 6, 0, 40, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 0, 0, 0, 20, 21, 22, 0, 23, 24, 0,
+ 0, 25, 0, 0, 0, 0, 0, 0, 0, 0,
+ 26, 27, 0, 0, 0, 0, 0, 0, 28, 0,
+ 0, 0, 0, 0, 0, 0, 29, 0, 30, 31,
+ 32, 33, 0, 34, 234, 0, 35, 0, 0, 0,
+ 36, 37, 38, 39, 0, 0, 1, 2, 3, 4,
+ 5, 6, 0, 40, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 0, 0, 0,
+ 20, 21, 22, 0, 23, 24, 0, 0, 25, 0,
+ 0, 0, 0, 0, 0, 0, 0, 26, 27, 0,
+ 0, 0, 0, 0, 0, 28, 0, 0, 0, 0,
+ 0, 0, 0, 29, 0, 30, 31, 32, 33, 0,
+ 34, 244, 0, 35, 0, 0, 0, 36, 37, 38,
+ 39, 0, 0, 1, 2, 3, 4, 5, 6, 0,
+ 40, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 20, 21, 22,
+ 0, 23, 24, 0, 0, 25, 0, 0, 0, 0,
+ 0, 0, 0, 0, 26, 27, 0, 0, 0, 0,
+ 0, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 29, 0, 30, 31, 32, 33, 0, 34, 324, 0,
+ 35, 0, 0, 0, 36, 37, 38, 39, 0, 0,
+ 1, 2, 3, 4, 5, 6, 0, 40, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 0, 0, 0, 20, 21, 22, 0, 23, 24,
+ 0, 0, 25, 0, 0, 0, 0, 0, 0, 0,
+ 0, 26, 27, 0, 0, 0, 0, 0, 0, 28,
+ 0, 0, 0, 0, 0, 0, 0, 29, 0, 30,
+ 31, 32, 33, 0, 34, 347, 0, 35, 0, 0,
+ 0, 36, 37, 38, 39, 0, 0, 1, 2, 3,
+ 4, 5, 6, 0, 40, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 0, 0,
+ 0, 20, 21, 22, 0, 23, 24, 0, 0, 25,
+ 0, 0, 0, 0, 0, 0, 0, 0, 26, 27,
+ 0, 0, 0, 0, 0, 0, 28, 0, 0, 0,
+ 0, 0, 0, 0, 29, 0, 30, 31, 32, 33,
+ 0, 34, 0, 0, 35, 0, 0, 0, 36, 37,
+ 38, 39, 0, 0, 1, 2, 3, 4, 5, 6,
+ 0, 40, 7, 8, 9, 10, 11, 90, 13, 108,
+ 15, 16, 17, 18, 19, 0, 0, 0, 20, 21,
+ 22, 0, 23, 24, 0, 0, 25, 0, 0, 0,
+ 0, 0, 0, 0, 0, 26, 27, 0, 0, 0,
+ 0, 0, 0, 28, 0, 0, 0, 0, 0, 0,
+ 0, 29, 0, 30, 31, 32, 33, 0, 34, 0,
+ 0, 35, 0, 0, 0, 36, 37, 38, 39, 0,
+ 0, 0, 0, 0, 0, 0, -130, 0, 40, -130,
+ -130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
+ -130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
+ -130, 0, 0, -130, -130, -130, 0, -130, -130, 0,
+ 0, -130, 0, 1, 2, 3, 4, 5, 0, 0,
+ 0, 0, 8, 200, 0, 0, 90, 0, 108, 15,
+ 0, 17, 0, 0, 0, 0, -130, 20, -130, -130,
+ 0, 0, 0, -130, -130, 0, 0, 0, 0, 0,
+ 0, 0, -130, -130, 26, 27, 0, 0, 0, 0,
+ 0, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 91, 0, 30, 31, 32, 33, 0, 92, 0, 0,
+ 35, 0, 0, 0, 36, 37, 38, 39, 1, 2,
+ 3, 4, 5, 0, 0, 0, 0, 8, 0, 0,
+ 0, 90, 0, 108, 15, 0, 17, 0, 0, 0,
+ 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+ 27, 0, 0, 0, 0, 0, 0, 28, 0, 0,
+ 0, 0, 0, 0, 0, 91, 0, 30, 31, 32,
+ 33, 0, 92, 0, 0, 35, 247, 0, 0, 36,
+ 37, 38, 39, 1, 2, 3, 4, 5, 0, 0,
+ 0, 0, 8, 0, 0, 0, 90, 0, 108, 15,
+ 0, 17, 0, 0, 0, 0, 0, 20, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 26, 27, 0, 0, 0, 0,
+ 0, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 91, 0, 30, 31, 32, 33, 250, 92, 0, 0,
+ 35, 0, 0, 0, 36, 37, 38, 39, 1, 2,
+ 3, 4, 5, 0, 0, 0, 0, 8, 0, 0,
+ 0, 90, 0, 108, 15, 0, 17, 0, 0, 0,
+ 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+ 27, 0, 0, 0, 0, 0, 0, 28, 0, 0,
+ 0, 0, 0, 0, 0, 91, 0, 30, 31, 32,
+ 33, 0, 92, 0, 0, 35, 336, 0, 0, 36,
+ 37, 38, 39, 1, 2, 3, 4, 5, 0, 0,
+ 0, 0, 8, 0, 0, 0, 12, 0, 108, 15,
+ 0, 17, 0, 0, 0, 0, 0, 20, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 26, 27, 0, 0, 0, 0,
+ 0, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 91, 0, 30, 31, 32, 33, 0, 92, 0, 0,
+ 35, 0, 0, 0, 36, 37, 38, 39, 1, 2,
+ 3, 4, 5, 0, 0, 0, 0, 8, 0, 0,
+ 0, 90, 0, 108, 15, 0, 17, 0, 0, 0,
+ 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+ 27, 0, 0, 0, 0, 0, 0, 28, 0, 0,
+ 0, 0, 0, 0, 0, 91, 0, 30, 31, 32,
+ 33, 0, 92, 0, 0, 35, 0, 0, 0, 36,
+ 37, 38, 39
+};
+
+static const short int yycheck[] =
+{
+ 18, 24, 0, 84, 98, 13, 1, 14, 15, 1,
+ 1, 141, 1, 20, 1, 23, 286, 147, 1, 26,
+ 27, 69, 1, 30, 31, 33, 25, 42, 1, 36,
+ 37, 38, 39, 1, 1, 6, 7, 64, 168, 87,
+ 65, 10, 61, 60, 46, 70, 65, 317, 73, 66,
+ 60, 78, 79, 65, 60, 61, 66, 138, 72, 65,
+ 14, 15, 192, 60, 194, 60, 20, 212, 60, 66,
+ 85, 216, 26, 27, 344, 60, 30, 31, 69, 209,
+ 69, 89, 36, 37, 38, 39, 69, 86, 358, 60,
+ 69, 93, 87, 6, 7, 87, 87, 68, 87, 68,
+ 87, 108, 69, 66, 87, 60, 69, 115, 87, 117,
+ 128, 119, 120, 243, 87, 66, 25, 26, 69, 87,
+ 87, 3, 4, 5, 6, 7, 69, 290, 71, 292,
+ 12, 40, 41, 296, 16, 89, 65, 74, 75, 21,
+ 148, 70, 66, 151, 73, 69, 65, 60, 177, 178,
+ 179, 180, 181, 182, 108, 68, 66, 65, 321, 69,
+ 323, 65, 169, 170, 171, 36, 37, 38, 39, 65,
+ 52, 80, 81, 67, 183, 184, 185, 186, 60, 309,
+ 69, 311, 64, 65, 314, 67, 83, 66, 70, 283,
+ 69, 66, 66, 66, 69, 69, 69, 66, 66, 66,
+ 69, 69, 69, 66, 66, 335, 69, 69, 69, 43,
+ 69, 72, 71, 236, 84, 169, 170, 171, 172, 173,
+ 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191, 25, 44,
+ 45, 44, 45, 69, 325, 71, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 44, 45, 174,
+ 175, 176, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 46, 47, 48, 82, 285, 286, 0,
+ 298, 65, 300, 86, 302, 33, 34, 68, 69, 172,
+ 173, 299, 220, 221, 86, 65, 294, 23, 72, 86,
+ 72, 72, 69, 60, 60, 60, 69, 315, 87, 317,
+ 34, 72, 65, 67, 65, 60, 60, 67, 25, 87,
+ 72, 24, 9, 66, 66, 343, 72, 87, 66, 68,
+ 348, 354, 8, 341, 245, 242, 344, 8, 356, 187,
+ 189, 188, 350, 373, 190, 200, 191, 352, 14, 365,
+ 358, 369, 237, 371, 372, 373, -1, -1, 376, -1,
+ -1, 379, 380, 1, -1, 3, 4, 5, 6, 7,
+ -1, -1, -1, -1, 12, -1, -1, -1, 16, -1,
+ 18, 19, -1, 21, -1, -1, -1, -1, -1, 27,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 44, 45, -1, -1,
+ -1, -1, -1, -1, 52, -1, -1, -1, -1, -1,
+ -1, -1, 60, -1, 62, 63, 64, 65, -1, 67,
+ -1, -1, 70, -1, -1, -1, 74, 75, 76, 77,
+ -1, -1, 3, 4, 5, 6, 7, 8, -1, 87,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, -1, -1, -1, 27, 28, 29, -1,
+ 31, 32, -1, -1, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, 44, 45, -1, -1, -1, -1, -1,
+ -1, 52, -1, -1, -1, -1, -1, -1, -1, 60,
+ -1, 62, 63, 64, 65, -1, 67, 68, -1, 70,
+ -1, -1, -1, 74, 75, 76, 77, -1, -1, 3,
+ 4, 5, 6, 7, 8, -1, 87, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ -1, -1, -1, 27, 28, 29, -1, 31, 32, -1,
+ -1, 35, -1, -1, -1, -1, -1, -1, -1, -1,
+ 44, 45, -1, -1, -1, -1, -1, -1, 52, -1,
+ -1, -1, -1, -1, -1, -1, 60, -1, 62, 63,
+ 64, 65, -1, 67, 68, -1, 70, -1, -1, -1,
+ 74, 75, 76, 77, -1, -1, 3, 4, 5, 6,
+ 7, 8, -1, 87, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, -1, -1, -1,
+ 27, 28, 29, -1, 31, 32, -1, -1, 35, -1,
+ -1, -1, -1, -1, -1, -1, -1, 44, 45, -1,
+ -1, -1, -1, -1, -1, 52, -1, -1, -1, -1,
+ -1, -1, -1, 60, -1, 62, 63, 64, 65, -1,
+ 67, 68, -1, 70, -1, -1, -1, 74, 75, 76,
+ 77, -1, -1, 3, 4, 5, 6, 7, 8, -1,
+ 87, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, -1, -1, -1, 27, 28, 29,
+ -1, 31, 32, -1, -1, 35, -1, -1, -1, -1,
+ -1, -1, -1, -1, 44, 45, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1, -1, -1,
+ 60, -1, 62, 63, 64, 65, -1, 67, 68, -1,
+ 70, -1, -1, -1, 74, 75, 76, 77, -1, -1,
+ 3, 4, 5, 6, 7, 8, -1, 87, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, -1, -1, -1, 27, 28, 29, -1, 31, 32,
+ -1, -1, 35, -1, -1, -1, -1, -1, -1, -1,
+ -1, 44, 45, -1, -1, -1, -1, -1, -1, 52,
+ -1, -1, -1, -1, -1, -1, -1, 60, -1, 62,
+ 63, 64, 65, -1, 67, 68, -1, 70, -1, -1,
+ -1, 74, 75, 76, 77, -1, -1, 3, 4, 5,
+ 6, 7, 8, -1, 87, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, -1, -1,
+ -1, 27, 28, 29, -1, 31, 32, -1, -1, 35,
+ -1, -1, -1, -1, -1, -1, -1, -1, 44, 45,
+ -1, -1, -1, -1, -1, -1, 52, -1, -1, -1,
+ -1, -1, -1, -1, 60, -1, 62, 63, 64, 65,
+ -1, 67, -1, -1, 70, -1, -1, -1, 74, 75,
+ 76, 77, -1, -1, 3, 4, 5, 6, 7, 8,
+ -1, 87, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, -1, -1, -1, 27, 28,
+ 29, -1, 31, 32, -1, -1, 35, -1, -1, -1,
+ -1, -1, -1, -1, -1, 44, 45, -1, -1, -1,
+ -1, -1, -1, 52, -1, -1, -1, -1, -1, -1,
+ -1, 60, -1, 62, 63, 64, 65, -1, 67, -1,
+ -1, 70, -1, -1, -1, 74, 75, 76, 77, -1,
+ -1, -1, -1, -1, -1, -1, 0, -1, 87, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, -1, -1, 27, 28, 29, -1, 31, 32, -1,
+ -1, 35, -1, 3, 4, 5, 6, 7, -1, -1,
+ -1, -1, 12, 13, -1, -1, 16, -1, 18, 19,
+ -1, 21, -1, -1, -1, -1, 60, 27, 62, 63,
+ -1, -1, -1, 67, 68, -1, -1, -1, -1, -1,
+ -1, -1, 76, 77, 44, 45, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1, -1, -1,
+ 60, -1, 62, 63, 64, 65, -1, 67, -1, -1,
+ 70, -1, -1, -1, 74, 75, 76, 77, 3, 4,
+ 5, 6, 7, -1, -1, -1, -1, 12, -1, -1,
+ -1, 16, -1, 18, 19, -1, 21, -1, -1, -1,
+ -1, -1, 27, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 44,
+ 45, -1, -1, -1, -1, -1, -1, 52, -1, -1,
+ -1, -1, -1, -1, -1, 60, -1, 62, 63, 64,
+ 65, -1, 67, -1, -1, 70, 71, -1, -1, 74,
+ 75, 76, 77, 3, 4, 5, 6, 7, -1, -1,
+ -1, -1, 12, -1, -1, -1, 16, -1, 18, 19,
+ -1, 21, -1, -1, -1, -1, -1, 27, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 44, 45, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1, -1, -1,
+ 60, -1, 62, 63, 64, 65, 66, 67, -1, -1,
+ 70, -1, -1, -1, 74, 75, 76, 77, 3, 4,
+ 5, 6, 7, -1, -1, -1, -1, 12, -1, -1,
+ -1, 16, -1, 18, 19, -1, 21, -1, -1, -1,
+ -1, -1, 27, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 44,
+ 45, -1, -1, -1, -1, -1, -1, 52, -1, -1,
+ -1, -1, -1, -1, -1, 60, -1, 62, 63, 64,
+ 65, -1, 67, -1, -1, 70, 71, -1, -1, 74,
+ 75, 76, 77, 3, 4, 5, 6, 7, -1, -1,
+ -1, -1, 12, -1, -1, -1, 16, -1, 18, 19,
+ -1, 21, -1, -1, -1, -1, -1, 27, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 44, 45, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1, -1, -1,
+ 60, -1, 62, 63, 64, 65, -1, 67, -1, -1,
+ 70, -1, -1, -1, 74, 75, 76, 77, 3, 4,
+ 5, 6, 7, -1, -1, -1, -1, 12, -1, -1,
+ -1, 16, -1, 18, 19, -1, 21, -1, -1, -1,
+ -1, -1, 27, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 44,
+ 45, -1, -1, -1, -1, -1, -1, 52, -1, -1,
+ -1, -1, -1, -1, -1, 60, -1, 62, 63, 64,
+ 65, -1, 67, -1, -1, 70, -1, -1, -1, 74,
+ 75, 76, 77
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 3, 4, 5, 6, 7, 8, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 27, 28, 29, 31, 32, 35, 44, 45, 52, 60,
+ 62, 63, 64, 65, 67, 70, 74, 75, 76, 77,
+ 87, 89, 90, 91, 97, 98, 99, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 118, 119, 120, 122, 125, 129, 130, 131,
+ 132, 134, 135, 136, 137, 138, 144, 145, 146, 147,
+ 150, 151, 152, 155, 156, 157, 1, 60, 87, 65,
+ 16, 60, 67, 97, 98, 60, 123, 124, 60, 126,
+ 127, 1, 60, 87, 60, 61, 65, 1, 18, 87,
+ 118, 102, 104, 151, 104, 65, 119, 65, 104, 65,
+ 65, 118, 67, 120, 1, 87, 104, 104, 72, 104,
+ 104, 118, 6, 7, 60, 68, 95, 96, 156, 69,
+ 92, 93, 94, 104, 104, 104, 104, 65, 70, 73,
+ 100, 70, 73, 100, 44, 45, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 86, 117, 64,
+ 78, 79, 74, 75, 46, 47, 48, 25, 26, 40,
+ 41, 80, 81, 36, 37, 38, 39, 82, 83, 84,
+ 43, 42, 85, 1, 69, 87, 0, 157, 1, 87,
+ 13, 102, 118, 133, 6, 7, 60, 68, 100, 86,
+ 128, 1, 69, 87, 128, 1, 69, 87, 1, 87,
+ 65, 65, 60, 66, 153, 1, 87, 118, 23, 118,
+ 118, 118, 1, 87, 68, 33, 34, 148, 149, 119,
+ 66, 68, 69, 72, 68, 69, 71, 71, 116, 69,
+ 66, 101, 116, 118, 60, 118, 60, 116, 104, 104,
+ 104, 105, 105, 106, 106, 106, 107, 107, 107, 107,
+ 107, 107, 108, 108, 108, 108, 109, 110, 111, 112,
+ 113, 116, 116, 60, 123, 25, 87, 116, 124, 124,
+ 66, 153, 66, 153, 67, 154, 66, 69, 66, 65,
+ 66, 66, 66, 65, 120, 149, 68, 96, 116, 93,
+ 66, 69, 71, 71, 72, 25, 128, 87, 118, 133,
+ 154, 66, 154, 66, 68, 156, 154, 60, 119, 118,
+ 119, 67, 139, 119, 60, 72, 71, 116, 116, 116,
+ 118, 25, 133, 66, 87, 154, 154, 68, 24, 66,
+ 9, 140, 141, 142, 66, 116, 66, 118, 87, 119,
+ 133, 119, 118, 10, 68, 143, 142, 120, 119, 66,
+ 133, 66, 72, 72, 140, 119, 66, 119, 119, 121,
+ 121, 68, 119, 119
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Type, Value, Location); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short int *bottom;
+ short int *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu), ",
+ yyrule - 1, yylno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname[yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ size_t yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+#endif /* YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ YYLTYPE *yylocationp;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+ (void) yylocationp;
+
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ YY_LOCATION_PRINT (yyoutput, *yylocationp);
+ YYFPRINTF (yyoutput, ": ");
+
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ YYLTYPE *yylocationp;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+ (void) yylocationp;
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+/* Location data for the look-ahead symbol. */
+YYLTYPE yylloc;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+ ;
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short int yyssa[YYINITDEPTH];
+ short int *yyss = yyssa;
+ short int *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+ /* The location stack. */
+ YYLTYPE yylsa[YYINITDEPTH];
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ /* The locations where the error started and ended. */
+ YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+ YYLTYPE yyloc;
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ yylsp = yyls;
+#if YYLTYPE_IS_TRIVIAL
+ /* Initialize the default location before parsing starts. */
+ yylloc.first_line = yylloc.last_line = 1;
+ yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short int *yyss1 = yyss;
+ YYLTYPE *yyls1 = yyls;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yyls1, yysize * sizeof (*yylsp),
+ &yystacksize);
+ yyls = yyls1;
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short int *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+ YYSTACK_RELOCATE (yyls);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+ yylsp = yyls + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a look-ahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+ *++yylsp = yylloc;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+ /* Default location. */
+ YYLLOC_DEFAULT (yyloc, yylsp - yylen, yylen);
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 169 "grammar.y"
+ { (yyval.node) = new NullNode(); ;}
+ break;
+
+ case 3:
+#line 170 "grammar.y"
+ { (yyval.node) = new BooleanNode(true); ;}
+ break;
+
+ case 4:
+#line 171 "grammar.y"
+ { (yyval.node) = new BooleanNode(false); ;}
+ break;
+
+ case 5:
+#line 172 "grammar.y"
+ { (yyval.node) = new NumberNode((yyvsp[0].dval)); ;}
+ break;
+
+ case 6:
+#line 173 "grammar.y"
+ { (yyval.node) = new StringNode((yyvsp[0].ustr)); ;}
+ break;
+
+ case 7:
+#line 174 "grammar.y"
+ { Lexer *l = Lexer::curr();
+ if (!l->scanRegExp()) YYABORT;
+ (yyval.node) = new RegExpNode(l->pattern,l->flags);;}
+ break;
+
+ case 8:
+#line 178 "grammar.y"
+ { Lexer *l = Lexer::curr();
+ if (!l->scanRegExp()) YYABORT;
+ (yyval.node) = new RegExpNode(UString('=')+l->pattern,l->flags);;}
+ break;
+
+ case 9:
+#line 184 "grammar.y"
+ { (yyval.node) = new ThisNode(); ;}
+ break;
+
+ case 10:
+#line 185 "grammar.y"
+ { (yyval.node) = new ResolveNode(*(yyvsp[0].ident)); ;}
+ break;
+
+ case 13:
+#line 188 "grammar.y"
+ { (yyval.node) = new GroupNode((yyvsp[-1].node)); ;}
+ break;
+
+ case 14:
+#line 189 "grammar.y"
+ { (yyval.node) = new ObjectLiteralNode(); ;}
+ break;
+
+ case 15:
+#line 190 "grammar.y"
+ { (yyval.node) = new ObjectLiteralNode((yyvsp[-1].plist)); ;}
+ break;
+
+ case 16:
+#line 191 "grammar.y"
+ { (yyval.node) = new ObjectLiteralNode((yyvsp[-2].plist)); ;}
+ break;
+
+ case 17:
+#line 195 "grammar.y"
+ { (yyval.node) = new ArrayNode((yyvsp[-1].ival)); ;}
+ break;
+
+ case 18:
+#line 196 "grammar.y"
+ { (yyval.node) = new ArrayNode((yyvsp[-1].elm)); ;}
+ break;
+
+ case 19:
+#line 197 "grammar.y"
+ { (yyval.node) = new ArrayNode((yyvsp[-1].ival), (yyvsp[-3].elm)); ;}
+ break;
+
+ case 20:
+#line 201 "grammar.y"
+ { (yyval.elm) = new ElementNode((yyvsp[-1].ival), (yyvsp[0].node)); ;}
+ break;
+
+ case 21:
+#line 203 "grammar.y"
+ { (yyval.elm) = new ElementNode((yyvsp[-3].elm), (yyvsp[-1].ival), (yyvsp[0].node)); ;}
+ break;
+
+ case 22:
+#line 207 "grammar.y"
+ { (yyval.ival) = 0; ;}
+ break;
+
+ case 24:
+#line 212 "grammar.y"
+ { (yyval.ival) = 1; ;}
+ break;
+
+ case 25:
+#line 213 "grammar.y"
+ { (yyval.ival) = (yyvsp[-1].ival) + 1; ;}
+ break;
+
+ case 26:
+#line 217 "grammar.y"
+ { (yyval.plist) = new PropertyValueNode((yyvsp[-2].pnode), (yyvsp[0].node)); ;}
+ break;
+
+ case 27:
+#line 219 "grammar.y"
+ { (yyval.plist) = new PropertyValueNode((yyvsp[-2].pnode), (yyvsp[0].node), (yyvsp[-4].plist)); ;}
+ break;
+
+ case 28:
+#line 223 "grammar.y"
+ { (yyval.pnode) = new PropertyNode(*(yyvsp[0].ident)); ;}
+ break;
+
+ case 29:
+#line 224 "grammar.y"
+ { (yyval.pnode) = new PropertyNode(Identifier(*(yyvsp[0].ustr))); ;}
+ break;
+
+ case 30:
+#line 225 "grammar.y"
+ { (yyval.pnode) = new PropertyNode((yyvsp[0].dval)); ;}
+ break;
+
+ case 33:
+#line 231 "grammar.y"
+ { (yyval.node) = new AccessorNode1((yyvsp[-3].node), (yyvsp[-1].node)); ;}
+ break;
+
+ case 34:
+#line 232 "grammar.y"
+ { (yyval.node) = new AccessorNode2((yyvsp[-2].node), *(yyvsp[0].ident)); ;}
+ break;
+
+ case 35:
+#line 233 "grammar.y"
+ { (yyval.node) = new NewExprNode((yyvsp[-1].node), (yyvsp[0].args)); ;}
+ break;
+
+ case 37:
+#line 238 "grammar.y"
+ { (yyval.node) = new NewExprNode((yyvsp[0].node)); ;}
+ break;
+
+ case 38:
+#line 242 "grammar.y"
+ { (yyval.node) = new FunctionCallNode((yyvsp[-1].node), (yyvsp[0].args)); ;}
+ break;
+
+ case 39:
+#line 243 "grammar.y"
+ { (yyval.node) = new FunctionCallNode((yyvsp[-1].node), (yyvsp[0].args)); ;}
+ break;
+
+ case 40:
+#line 244 "grammar.y"
+ { (yyval.node) = new AccessorNode1((yyvsp[-3].node), (yyvsp[-1].node)); ;}
+ break;
+
+ case 41:
+#line 245 "grammar.y"
+ { (yyval.node) = new AccessorNode2((yyvsp[-2].node), *(yyvsp[0].ident)); ;}
+ break;
+
+ case 42:
+#line 249 "grammar.y"
+ { (yyval.args) = new ArgumentsNode(); ;}
+ break;
+
+ case 43:
+#line 250 "grammar.y"
+ { (yyval.args) = new ArgumentsNode((yyvsp[-1].alist)); ;}
+ break;
+
+ case 44:
+#line 254 "grammar.y"
+ { (yyval.alist) = new ArgumentListNode((yyvsp[0].node)); ;}
+ break;
+
+ case 45:
+#line 255 "grammar.y"
+ { (yyval.alist) = new ArgumentListNode((yyvsp[-2].alist), (yyvsp[0].node)); ;}
+ break;
+
+ case 49:
+#line 265 "grammar.y"
+ { (yyval.node) = new PostfixNode((yyvsp[-1].node), OpPlusPlus); ;}
+ break;
+
+ case 50:
+#line 266 "grammar.y"
+ { (yyval.node) = new PostfixNode((yyvsp[-1].node), OpMinusMinus); ;}
+ break;
+
+ case 52:
+#line 271 "grammar.y"
+ { (yyval.node) = new DeleteNode((yyvsp[0].node)); ;}
+ break;
+
+ case 53:
+#line 272 "grammar.y"
+ { (yyval.node) = new VoidNode((yyvsp[0].node)); ;}
+ break;
+
+ case 54:
+#line 273 "grammar.y"
+ { (yyval.node) = new TypeOfNode((yyvsp[0].node)); ;}
+ break;
+
+ case 55:
+#line 274 "grammar.y"
+ { (yyval.node) = new PrefixNode(OpPlusPlus, (yyvsp[0].node)); ;}
+ break;
+
+ case 56:
+#line 275 "grammar.y"
+ { (yyval.node) = new PrefixNode(OpPlusPlus, (yyvsp[0].node)); ;}
+ break;
+
+ case 57:
+#line 276 "grammar.y"
+ { (yyval.node) = new PrefixNode(OpMinusMinus, (yyvsp[0].node)); ;}
+ break;
+
+ case 58:
+#line 277 "grammar.y"
+ { (yyval.node) = new PrefixNode(OpMinusMinus, (yyvsp[0].node)); ;}
+ break;
+
+ case 59:
+#line 278 "grammar.y"
+ { (yyval.node) = new UnaryPlusNode((yyvsp[0].node)); ;}
+ break;
+
+ case 60:
+#line 279 "grammar.y"
+ { (yyval.node) = new NegateNode((yyvsp[0].node)); ;}
+ break;
+
+ case 61:
+#line 280 "grammar.y"
+ { (yyval.node) = new BitwiseNotNode((yyvsp[0].node)); ;}
+ break;
+
+ case 62:
+#line 281 "grammar.y"
+ { (yyval.node) = new LogicalNotNode((yyvsp[0].node)); ;}
+ break;
+
+ case 64:
+#line 286 "grammar.y"
+ { (yyval.node) = new MultNode((yyvsp[-2].node), (yyvsp[0].node), '*'); ;}
+ break;
+
+ case 65:
+#line 287 "grammar.y"
+ { (yyval.node) = new MultNode((yyvsp[-2].node), (yyvsp[0].node), '/'); ;}
+ break;
+
+ case 66:
+#line 288 "grammar.y"
+ { (yyval.node) = new MultNode((yyvsp[-2].node),(yyvsp[0].node),'%'); ;}
+ break;
+
+ case 68:
+#line 293 "grammar.y"
+ { (yyval.node) = AddNode::create((yyvsp[-2].node), (yyvsp[0].node), '+'); ;}
+ break;
+
+ case 69:
+#line 294 "grammar.y"
+ { (yyval.node) = AddNode::create((yyvsp[-2].node), (yyvsp[0].node), '-'); ;}
+ break;
+
+ case 71:
+#line 299 "grammar.y"
+ { (yyval.node) = new ShiftNode((yyvsp[-2].node), OpLShift, (yyvsp[0].node)); ;}
+ break;
+
+ case 72:
+#line 300 "grammar.y"
+ { (yyval.node) = new ShiftNode((yyvsp[-2].node), OpRShift, (yyvsp[0].node)); ;}
+ break;
+
+ case 73:
+#line 301 "grammar.y"
+ { (yyval.node) = new ShiftNode((yyvsp[-2].node), OpURShift, (yyvsp[0].node)); ;}
+ break;
+
+ case 75:
+#line 307 "grammar.y"
+ { (yyval.node) = new RelationalNode((yyvsp[-2].node), OpLess, (yyvsp[0].node)); ;}
+ break;
+
+ case 76:
+#line 309 "grammar.y"
+ { (yyval.node) = new RelationalNode((yyvsp[-2].node), OpGreater, (yyvsp[0].node)); ;}
+ break;
+
+ case 77:
+#line 311 "grammar.y"
+ { (yyval.node) = new RelationalNode((yyvsp[-2].node), OpLessEq, (yyvsp[0].node)); ;}
+ break;
+
+ case 78:
+#line 313 "grammar.y"
+ { (yyval.node) = new RelationalNode((yyvsp[-2].node), OpGreaterEq, (yyvsp[0].node)); ;}
+ break;
+
+ case 79:
+#line 315 "grammar.y"
+ { (yyval.node) = new RelationalNode((yyvsp[-2].node), OpInstanceOf, (yyvsp[0].node)); ;}
+ break;
+
+ case 80:
+#line 317 "grammar.y"
+ { (yyval.node) = new RelationalNode((yyvsp[-2].node), OpIn, (yyvsp[0].node)); ;}
+ break;
+
+ case 82:
+#line 322 "grammar.y"
+ { (yyval.node) = new EqualNode((yyvsp[-2].node), OpEqEq, (yyvsp[0].node)); ;}
+ break;
+
+ case 83:
+#line 323 "grammar.y"
+ { (yyval.node) = new EqualNode((yyvsp[-2].node), OpNotEq, (yyvsp[0].node)); ;}
+ break;
+
+ case 84:
+#line 324 "grammar.y"
+ { (yyval.node) = new EqualNode((yyvsp[-2].node), OpStrEq, (yyvsp[0].node)); ;}
+ break;
+
+ case 85:
+#line 325 "grammar.y"
+ { (yyval.node) = new EqualNode((yyvsp[-2].node), OpStrNEq, (yyvsp[0].node));;}
+ break;
+
+ case 87:
+#line 330 "grammar.y"
+ { (yyval.node) = new BitOperNode((yyvsp[-2].node), OpBitAnd, (yyvsp[0].node)); ;}
+ break;
+
+ case 89:
+#line 335 "grammar.y"
+ { (yyval.node) = new BitOperNode((yyvsp[-2].node), OpBitXOr, (yyvsp[0].node)); ;}
+ break;
+
+ case 91:
+#line 340 "grammar.y"
+ { (yyval.node) = new BitOperNode((yyvsp[-2].node), OpBitOr, (yyvsp[0].node)); ;}
+ break;
+
+ case 93:
+#line 346 "grammar.y"
+ { (yyval.node) = new BinaryLogicalNode((yyvsp[-2].node), OpAnd, (yyvsp[0].node)); ;}
+ break;
+
+ case 95:
+#line 352 "grammar.y"
+ { (yyval.node) = new BinaryLogicalNode((yyvsp[-2].node), OpOr, (yyvsp[0].node)); ;}
+ break;
+
+ case 97:
+#line 358 "grammar.y"
+ { (yyval.node) = new ConditionalNode((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); ;}
+ break;
+
+ case 99:
+#line 364 "grammar.y"
+ { (yyval.node) = new AssignNode((yyvsp[-2].node), (yyvsp[-1].op), (yyvsp[0].node));;}
+ break;
+
+ case 100:
+#line 368 "grammar.y"
+ { (yyval.op) = OpEqual; ;}
+ break;
+
+ case 101:
+#line 369 "grammar.y"
+ { (yyval.op) = OpPlusEq; ;}
+ break;
+
+ case 102:
+#line 370 "grammar.y"
+ { (yyval.op) = OpMinusEq; ;}
+ break;
+
+ case 103:
+#line 371 "grammar.y"
+ { (yyval.op) = OpMultEq; ;}
+ break;
+
+ case 104:
+#line 372 "grammar.y"
+ { (yyval.op) = OpDivEq; ;}
+ break;
+
+ case 105:
+#line 373 "grammar.y"
+ { (yyval.op) = OpLShift; ;}
+ break;
+
+ case 106:
+#line 374 "grammar.y"
+ { (yyval.op) = OpRShift; ;}
+ break;
+
+ case 107:
+#line 375 "grammar.y"
+ { (yyval.op) = OpURShift; ;}
+ break;
+
+ case 108:
+#line 376 "grammar.y"
+ { (yyval.op) = OpAndEq; ;}
+ break;
+
+ case 109:
+#line 377 "grammar.y"
+ { (yyval.op) = OpXOrEq; ;}
+ break;
+
+ case 110:
+#line 378 "grammar.y"
+ { (yyval.op) = OpOrEq; ;}
+ break;
+
+ case 111:
+#line 379 "grammar.y"
+ { (yyval.op) = OpModEq; ;}
+ break;
+
+ case 113:
+#line 384 "grammar.y"
+ { (yyval.node) = new CommaNode((yyvsp[-2].node), (yyvsp[0].node)); ;}
+ break;
+
+ case 130:
+#line 407 "grammar.y"
+ { (yyval.stat) = new BlockNode(0); DBG((yyval.stat), (yylsp[0]), (yylsp[0])); ;}
+ break;
+
+ case 131:
+#line 408 "grammar.y"
+ { (yyval.stat) = new BlockNode((yyvsp[-1].srcs)); DBG((yyval.stat), (yylsp[0]), (yylsp[0])); ;}
+ break;
+
+ case 132:
+#line 412 "grammar.y"
+ { (yyval.slist) = new StatListNode((yyvsp[0].stat)); ;}
+ break;
+
+ case 133:
+#line 413 "grammar.y"
+ { (yyval.slist) = new StatListNode((yyvsp[-1].slist), (yyvsp[0].stat)); ;}
+ break;
+
+ case 134:
+#line 417 "grammar.y"
+ { (yyval.stat) = new VarStatementNode((yyvsp[-1].vlist));
+ DBG((yyval.stat), (yylsp[-2]), (yylsp[0])); ;}
+ break;
+
+ case 135:
+#line 419 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new VarStatementNode((yyvsp[-1].vlist));
+ DBG((yyval.stat), (yylsp[-2]), (yylsp[-1]));
+ } else {
+ YYABORT;
+ }
+ ;}
+ break;
+
+ case 136:
+#line 429 "grammar.y"
+ { (yyval.vlist) = new VarDeclListNode((yyvsp[0].decl)); ;}
+ break;
+
+ case 137:
+#line 431 "grammar.y"
+ { (yyval.vlist) = new VarDeclListNode((yyvsp[-2].vlist), (yyvsp[0].decl)); ;}
+ break;
+
+ case 138:
+#line 435 "grammar.y"
+ { (yyval.decl) = new VarDeclNode(*(yyvsp[0].ident), 0, VarDeclNode::Variable); ;}
+ break;
+
+ case 139:
+#line 436 "grammar.y"
+ { (yyval.decl) = new VarDeclNode(*(yyvsp[-1].ident), (yyvsp[0].init), VarDeclNode::Variable); ;}
+ break;
+
+ case 140:
+#line 440 "grammar.y"
+ { (yyval.stat) = new VarStatementNode((yyvsp[-1].vlist));
+ DBG((yyval.stat), (yylsp[-2]), (yylsp[0])); ;}
+ break;
+
+ case 141:
+#line 442 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new VarStatementNode((yyvsp[-1].vlist));
+ DBG((yyval.stat), (yylsp[-2]), (yylsp[-1]));
+ } else {
+ YYABORT;
+ }
+ ;}
+ break;
+
+ case 142:
+#line 452 "grammar.y"
+ { (yyval.vlist) = new VarDeclListNode((yyvsp[0].decl)); ;}
+ break;
+
+ case 143:
+#line 454 "grammar.y"
+ { (yyval.vlist) = new VarDeclListNode((yyvsp[-2].vlist), (yyvsp[0].decl)); ;}
+ break;
+
+ case 144:
+#line 458 "grammar.y"
+ { (yyval.decl) = new VarDeclNode(*(yyvsp[0].ident), 0, VarDeclNode::Constant); ;}
+ break;
+
+ case 145:
+#line 459 "grammar.y"
+ { (yyval.decl) = new VarDeclNode(*(yyvsp[-1].ident), (yyvsp[0].init), VarDeclNode::Constant); ;}
+ break;
+
+ case 146:
+#line 463 "grammar.y"
+ { (yyval.init) = new AssignExprNode((yyvsp[0].node)); ;}
+ break;
+
+ case 147:
+#line 467 "grammar.y"
+ { (yyval.stat) = new EmptyStatementNode(); DBG((yyval.stat), (yylsp[0]), (yylsp[0])); ;}
+ break;
+
+ case 148:
+#line 471 "grammar.y"
+ { (yyval.stat) = new ExprStatementNode((yyvsp[-1].node));
+ DBG((yyval.stat), (yylsp[-1]), (yylsp[0])); ;}
+ break;
+
+ case 149:
+#line 473 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new ExprStatementNode((yyvsp[-1].node));
+ DBG((yyval.stat), (yylsp[-1]), (yylsp[-1]));
+ } else
+ YYABORT; ;}
+ break;
+
+ case 150:
+#line 481 "grammar.y"
+ { (yyval.stat) = new IfNode((yyvsp[-2].node),(yyvsp[0].stat),0);DBG((yyval.stat),(yylsp[-4]),(yylsp[-1])); ;}
+ break;
+
+ case 151:
+#line 483 "grammar.y"
+ { (yyval.stat) = new IfNode((yyvsp[-4].node),(yyvsp[-2].stat),(yyvsp[0].stat));DBG((yyval.stat),(yylsp[-6]),(yylsp[-3])); ;}
+ break;
+
+ case 152:
+#line 487 "grammar.y"
+ { (yyval.stat)=new DoWhileNode((yyvsp[-4].stat),(yyvsp[-1].node));DBG((yyval.stat),(yylsp[-5]),(yylsp[-3]));;}
+ break;
+
+ case 153:
+#line 488 "grammar.y"
+ { (yyval.stat) = new WhileNode((yyvsp[-2].node),(yyvsp[0].stat));DBG((yyval.stat),(yylsp[-4]),(yylsp[-1])); ;}
+ break;
+
+ case 154:
+#line 490 "grammar.y"
+ { (yyval.stat) = new ForNode((yyvsp[-6].node),(yyvsp[-4].node),(yyvsp[-2].node),(yyvsp[0].stat));
+ DBG((yyval.stat),(yylsp[-8]),(yylsp[-1])); ;}
+ break;
+
+ case 155:
+#line 493 "grammar.y"
+ { (yyval.stat) = new ForNode((yyvsp[-6].vlist),(yyvsp[-4].node),(yyvsp[-2].node),(yyvsp[0].stat));
+ DBG((yyval.stat),(yylsp[-9]),(yylsp[-1])); ;}
+ break;
+
+ case 156:
+#line 496 "grammar.y"
+ { (yyval.stat) = new ForInNode((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].stat));
+ DBG((yyval.stat),(yylsp[-6]),(yylsp[-1])); ;}
+ break;
+
+ case 157:
+#line 499 "grammar.y"
+ { (yyval.stat) = new ForInNode(*(yyvsp[-4].ident),0,(yyvsp[-2].node),(yyvsp[0].stat));
+ DBG((yyval.stat),(yylsp[-7]),(yylsp[-1])); ;}
+ break;
+
+ case 158:
+#line 502 "grammar.y"
+ { (yyval.stat) = new ForInNode(*(yyvsp[-5].ident),(yyvsp[-4].init),(yyvsp[-2].node),(yyvsp[0].stat));
+ DBG((yyval.stat),(yylsp[-8]),(yylsp[-1])); ;}
+ break;
+
+ case 159:
+#line 507 "grammar.y"
+ { (yyval.node) = 0; ;}
+ break;
+
+ case 161:
+#line 512 "grammar.y"
+ { (yyval.stat) = new ContinueNode(); DBG((yyval.stat),(yylsp[-1]),(yylsp[0])); ;}
+ break;
+
+ case 162:
+#line 513 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new ContinueNode(); DBG((yyval.stat),(yylsp[-1]),(yylsp[0]));
+ } else
+ YYABORT; ;}
+ break;
+
+ case 163:
+#line 517 "grammar.y"
+ { (yyval.stat) = new ContinueNode(*(yyvsp[-1].ident)); DBG((yyval.stat),(yylsp[-2]),(yylsp[0])); ;}
+ break;
+
+ case 164:
+#line 518 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new ContinueNode(*(yyvsp[-1].ident));DBG((yyval.stat),(yylsp[-2]),(yylsp[-1]));
+ } else
+ YYABORT; ;}
+ break;
+
+ case 165:
+#line 525 "grammar.y"
+ { (yyval.stat) = new BreakNode();DBG((yyval.stat),(yylsp[-1]),(yylsp[0])); ;}
+ break;
+
+ case 166:
+#line 526 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new BreakNode(); DBG((yyval.stat),(yylsp[-1]),(yylsp[-1]));
+ } else
+ YYABORT; ;}
+ break;
+
+ case 167:
+#line 530 "grammar.y"
+ { (yyval.stat) = new BreakNode(*(yyvsp[-1].ident)); DBG((yyval.stat),(yylsp[-2]),(yylsp[0])); ;}
+ break;
+
+ case 168:
+#line 531 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new BreakNode(*(yyvsp[-1].ident)); DBG((yyval.stat),(yylsp[-2]),(yylsp[-1]));
+ } else
+ YYABORT;
+ ;}
+ break;
+
+ case 169:
+#line 539 "grammar.y"
+ { (yyval.stat) = new ReturnNode(0); DBG((yyval.stat),(yylsp[-1]),(yylsp[0])); ;}
+ break;
+
+ case 170:
+#line 540 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new ReturnNode(0); DBG((yyval.stat),(yylsp[-1]),(yylsp[-1]));
+ } else
+ YYABORT; ;}
+ break;
+
+ case 171:
+#line 544 "grammar.y"
+ { (yyval.stat) = new ReturnNode((yyvsp[-1].node)); DBG((yyval.stat),(yylsp[-2]),(yylsp[0])); ;}
+ break;
+
+ case 172:
+#line 545 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new ReturnNode((yyvsp[-1].node)); DBG((yyval.stat),(yylsp[-2]),(yylsp[-2]));
+ }
+ else
+ YYABORT; ;}
+ break;
+
+ case 173:
+#line 553 "grammar.y"
+ { (yyval.stat) = new WithNode((yyvsp[-2].node),(yyvsp[0].stat));
+ DBG((yyval.stat), (yylsp[-4]), (yylsp[-1])); ;}
+ break;
+
+ case 174:
+#line 558 "grammar.y"
+ { (yyval.stat) = new SwitchNode((yyvsp[-2].node), (yyvsp[0].cblk));
+ DBG((yyval.stat), (yylsp[-4]), (yylsp[-1])); ;}
+ break;
+
+ case 175:
+#line 563 "grammar.y"
+ { (yyval.cblk) = new CaseBlockNode((yyvsp[-1].clist), 0, 0); ;}
+ break;
+
+ case 176:
+#line 565 "grammar.y"
+ { (yyval.cblk) = new CaseBlockNode((yyvsp[-3].clist), (yyvsp[-2].ccl), (yyvsp[-1].clist)); ;}
+ break;
+
+ case 177:
+#line 569 "grammar.y"
+ { (yyval.clist) = 0; ;}
+ break;
+
+ case 179:
+#line 574 "grammar.y"
+ { (yyval.clist) = new ClauseListNode((yyvsp[0].ccl)); ;}
+ break;
+
+ case 180:
+#line 575 "grammar.y"
+ { (yyval.clist) = new ClauseListNode((yyvsp[-1].clist), (yyvsp[0].ccl)); ;}
+ break;
+
+ case 181:
+#line 579 "grammar.y"
+ { (yyval.ccl) = new CaseClauseNode((yyvsp[-1].node)); ;}
+ break;
+
+ case 182:
+#line 580 "grammar.y"
+ { (yyval.ccl) = new CaseClauseNode((yyvsp[-2].node), (yyvsp[0].slist)); ;}
+ break;
+
+ case 183:
+#line 584 "grammar.y"
+ { (yyval.ccl) = new CaseClauseNode(0); ;}
+ break;
+
+ case 184:
+#line 585 "grammar.y"
+ { (yyval.ccl) = new CaseClauseNode(0, (yyvsp[0].slist)); ;}
+ break;
+
+ case 185:
+#line 589 "grammar.y"
+ { (yyvsp[0].stat)->pushLabel(*(yyvsp[-2].ident));
+ (yyval.stat) = new LabelNode(*(yyvsp[-2].ident), (yyvsp[0].stat)); DBG((yyval.stat),(yylsp[-2]),(yylsp[-1])); ;}
+ break;
+
+ case 186:
+#line 594 "grammar.y"
+ { (yyval.stat) = new ThrowNode((yyvsp[-1].node)); DBG((yyval.stat),(yylsp[-2]),(yylsp[0])); ;}
+ break;
+
+ case 187:
+#line 595 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new ThrowNode((yyvsp[-1].node)); DBG((yyval.stat),(yylsp[-2]),(yylsp[-2]));
+ } else {
+ YYABORT; } ;}
+ break;
+
+ case 188:
+#line 602 "grammar.y"
+ { (yyval.stat) = new TryNode((yyvsp[-1].stat), (yyvsp[0].cnode)); DBG((yyval.stat),(yylsp[-2]),(yylsp[-2])); ;}
+ break;
+
+ case 189:
+#line 603 "grammar.y"
+ { (yyval.stat) = new TryNode((yyvsp[-1].stat), (yyvsp[0].fnode)); DBG((yyval.stat),(yylsp[-2]),(yylsp[-2])); ;}
+ break;
+
+ case 190:
+#line 604 "grammar.y"
+ { (yyval.stat) = new TryNode((yyvsp[-2].stat), (yyvsp[-1].cnode), (yyvsp[0].fnode)); DBG((yyval.stat),(yylsp[-3]),(yylsp[-3])); ;}
+ break;
+
+ case 191:
+#line 608 "grammar.y"
+ { (yyval.stat) = new EmptyStatementNode(); DBG((yyval.stat), (yylsp[-1]), (yylsp[0])); ;}
+ break;
+
+ case 192:
+#line 609 "grammar.y"
+ { if (automatic()) {
+ (yyval.stat) = new EmptyStatementNode();
+ DBG((yyval.stat), (yylsp[-1]), (yylsp[-1]));
+ } else {
+ YYABORT; } ;}
+ break;
+
+ case 193:
+#line 618 "grammar.y"
+ { CatchNode *c; (yyval.cnode) = c = new CatchNode(*(yyvsp[-2].ident), (yyvsp[0].stat));
+ DBG(c,(yylsp[-4]),(yylsp[-1])); ;}
+ break;
+
+ case 194:
+#line 623 "grammar.y"
+ { FinallyNode *f; (yyval.fnode) = f = new FinallyNode((yyvsp[0].stat)); DBG(f,(yylsp[-1]),(yylsp[-1])); ;}
+ break;
+
+ case 196:
+#line 629 "grammar.y"
+ { (yyval.func) = (yyvsp[0].func); ;}
+ break;
+
+ case 197:
+#line 633 "grammar.y"
+ { (yyval.func) = new FuncDeclNode(*(yyvsp[-3].ident), (yyvsp[0].body)); DBG((yyval.func),(yylsp[-4]),(yylsp[-1])); ;}
+ break;
+
+ case 198:
+#line 635 "grammar.y"
+ { (yyval.func) = new FuncDeclNode(*(yyvsp[-4].ident), (yyvsp[-2].param), (yyvsp[0].body)); DBG((yyval.func),(yylsp[-5]),(yylsp[-1])); ;}
+ break;
+
+ case 199:
+#line 640 "grammar.y"
+ { (yyval.node) = new FuncExprNode(Identifier::null(), (yyvsp[0].body)); ;}
+ break;
+
+ case 200:
+#line 642 "grammar.y"
+ { (yyval.node) = new FuncExprNode(Identifier::null(), (yyvsp[-2].param), (yyvsp[0].body)); ;}
+ break;
+
+ case 201:
+#line 644 "grammar.y"
+ { (yyval.node) = new FuncExprNode(*(yyvsp[-3].ident), (yyvsp[0].body)); ;}
+ break;
+
+ case 202:
+#line 646 "grammar.y"
+ { (yyval.node) = new FuncExprNode(*(yyvsp[-4].ident), (yyvsp[-2].param), (yyvsp[0].body)); ;}
+ break;
+
+ case 203:
+#line 650 "grammar.y"
+ { (yyval.param) = new ParameterNode(*(yyvsp[0].ident)); ;}
+ break;
+
+ case 204:
+#line 651 "grammar.y"
+ { (yyval.param) = new ParameterNode((yyvsp[-2].param), *(yyvsp[0].ident)); ;}
+ break;
+
+ case 205:
+#line 655 "grammar.y"
+ { (yyval.body) = new FunctionBodyNode(0);
+ DBG((yyval.body), (yylsp[-1]), (yylsp[0]));;}
+ break;
+
+ case 206:
+#line 657 "grammar.y"
+ { (yyval.body) = new FunctionBodyNode((yyvsp[-1].srcs));
+ DBG((yyval.body), (yylsp[-2]), (yylsp[0]));;}
+ break;
+
+ case 207:
+#line 662 "grammar.y"
+ { (yyval.prog) = new FunctionBodyNode(0);
+ (yyval.prog)->setLoc(0, 0, Parser::source);
+ Parser::progNode = (yyval.prog); ;}
+ break;
+
+ case 208:
+#line 665 "grammar.y"
+ { (yyval.prog) = new FunctionBodyNode((yyvsp[0].srcs));
+ Parser::progNode = (yyval.prog); ;}
+ break;
+
+ case 209:
+#line 670 "grammar.y"
+ { (yyval.srcs) = new SourceElementsNode((yyvsp[0].stat)); ;}
+ break;
+
+ case 210:
+#line 671 "grammar.y"
+ { (yyval.srcs) = new SourceElementsNode((yyvsp[-1].srcs), (yyvsp[0].stat)); ;}
+ break;
+
+ case 211:
+#line 675 "grammar.y"
+ { (yyval.stat) = (yyvsp[0].stat); ;}
+ break;
+
+ case 212:
+#line 676 "grammar.y"
+ { (yyval.stat) = (yyvsp[0].func); ;}
+ break;
+
+
+ default: break;
+ }
+
+/* Line 1126 of yacc.c. */
+#line 2843 "grammar.tab.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+ yylsp -= yylen;
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+ *++yylsp = yyloc;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ char *yymsg = 0;
+# define YYERROR_VERBOSE_ARGS_MAXIMUM 5
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+#if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+#endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= yysize1 < yysize;
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= yysize1 < yysize;
+ yysize = yysize1;
+
+ if (!yysize_overflow && yysize <= YYSTACK_ALLOC_MAXIMUM)
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyf))
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ goto yyexhaustedlab;
+ }
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror (YY_("syntax error"));
+ }
+
+ yyerror_range[0] = yylloc;
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding", yytoken, &yylval, &yylloc);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (0)
+ goto yyerrorlab;
+
+ yyerror_range[0] = yylsp[1-yylen];
+ yylsp -= yylen;
+ yyvsp -= yylen;
+ yyssp -= yylen;
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ yyerror_range[0] = *yylsp;
+ yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp);
+ YYPOPSTACK;
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+ yyerror_range[1] = yylloc;
+ /* Using YYLLOC is tempting, but would change the location of
+ the look-ahead. YYLOC is available though. */
+ YYLLOC_DEFAULT (yyloc, yyerror_range - 1, 2);
+ *++yylsp = yyloc;
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, &yylloc);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, yylsp);
+ YYPOPSTACK;
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 679 "grammar.y"
+
+
+int yyerror (const char * /* s */) /* Called by yyparse on error */
+{
+ // fprintf(stderr, "ERROR: %s at line %d\n",
+ // s, KJS::Lexer::curr()->lineNo());
+ return 1;
+}
+
+/* may we automatically insert a semicolon ? */
+bool automatic()
+{
+ if (Lexer::curr()->hadError())
+ return false;
+ if (yychar == '}' || yychar == 0)
+ return true;
+ else if (Lexer::curr()->prevTerminator())
+ return true;
+
+ return false;
+}
+
diff --git a/kjs/grammar.h b/kjs/grammar.h
new file mode 100644
index 000000000..2e04b89aa
--- /dev/null
+++ b/kjs/grammar.h
@@ -0,0 +1,215 @@
+/* A Bison parser, made by GNU Bison 2.1. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NULLTOKEN = 258,
+ TRUETOKEN = 259,
+ FALSETOKEN = 260,
+ STRING = 261,
+ NUMBER = 262,
+ BREAK = 263,
+ CASE = 264,
+ DEFAULT = 265,
+ FOR = 266,
+ NEW = 267,
+ VAR = 268,
+ CONST = 269,
+ CONTINUE = 270,
+ FUNCTION = 271,
+ RETURN = 272,
+ VOID = 273,
+ DELETE = 274,
+ IF = 275,
+ THIS = 276,
+ DO = 277,
+ WHILE = 278,
+ ELSE = 279,
+ IN = 280,
+ INSTANCEOF = 281,
+ TYPEOF = 282,
+ SWITCH = 283,
+ WITH = 284,
+ RESERVED = 285,
+ THROW = 286,
+ TRY = 287,
+ CATCH = 288,
+ FINALLY = 289,
+ DEBUGGER = 290,
+ EQEQ = 291,
+ NE = 292,
+ STREQ = 293,
+ STRNEQ = 294,
+ LE = 295,
+ GE = 296,
+ OR = 297,
+ AND = 298,
+ PLUSPLUS = 299,
+ MINUSMINUS = 300,
+ LSHIFT = 301,
+ RSHIFT = 302,
+ URSHIFT = 303,
+ PLUSEQUAL = 304,
+ MINUSEQUAL = 305,
+ MULTEQUAL = 306,
+ DIVEQUAL = 307,
+ LSHIFTEQUAL = 308,
+ RSHIFTEQUAL = 309,
+ URSHIFTEQUAL = 310,
+ ANDEQUAL = 311,
+ MODEQUAL = 312,
+ XOREQUAL = 313,
+ OREQUAL = 314,
+ IDENT = 315,
+ FUNCEXPRIDENT = 316,
+ AUTOPLUSPLUS = 317,
+ AUTOMINUSMINUS = 318
+ };
+#endif
+/* Tokens. */
+#define NULLTOKEN 258
+#define TRUETOKEN 259
+#define FALSETOKEN 260
+#define STRING 261
+#define NUMBER 262
+#define BREAK 263
+#define CASE 264
+#define DEFAULT 265
+#define FOR 266
+#define NEW 267
+#define VAR 268
+#define CONST 269
+#define CONTINUE 270
+#define FUNCTION 271
+#define RETURN 272
+#define VOID 273
+#define DELETE 274
+#define IF 275
+#define THIS 276
+#define DO 277
+#define WHILE 278
+#define ELSE 279
+#define IN 280
+#define INSTANCEOF 281
+#define TYPEOF 282
+#define SWITCH 283
+#define WITH 284
+#define RESERVED 285
+#define THROW 286
+#define TRY 287
+#define CATCH 288
+#define FINALLY 289
+#define DEBUGGER 290
+#define EQEQ 291
+#define NE 292
+#define STREQ 293
+#define STRNEQ 294
+#define LE 295
+#define GE 296
+#define OR 297
+#define AND 298
+#define PLUSPLUS 299
+#define MINUSMINUS 300
+#define LSHIFT 301
+#define RSHIFT 302
+#define URSHIFT 303
+#define PLUSEQUAL 304
+#define MINUSEQUAL 305
+#define MULTEQUAL 306
+#define DIVEQUAL 307
+#define LSHIFTEQUAL 308
+#define RSHIFTEQUAL 309
+#define URSHIFTEQUAL 310
+#define ANDEQUAL 311
+#define MODEQUAL 312
+#define XOREQUAL 313
+#define OREQUAL 314
+#define IDENT 315
+#define FUNCEXPRIDENT 316
+#define AUTOPLUSPLUS 317
+#define AUTOMINUSMINUS 318
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 52 "grammar.y"
+typedef union YYSTYPE {
+ int ival;
+ double dval;
+ UString *ustr;
+ Identifier *ident;
+ Node *node;
+ StatementNode *stat;
+ ParameterNode *param;
+ FunctionBodyNode *body;
+ FuncDeclNode *func;
+ FunctionBodyNode *prog;
+ AssignExprNode *init;
+ SourceElementsNode *srcs;
+ StatListNode *slist;
+ ArgumentsNode *args;
+ ArgumentListNode *alist;
+ VarDeclNode *decl;
+ VarDeclListNode *vlist;
+ CaseBlockNode *cblk;
+ ClauseListNode *clist;
+ CaseClauseNode *ccl;
+ ElementNode *elm;
+ Operator op;
+ PropertyValueNode *plist;
+ PropertyNode *pnode;
+ CatchNode *cnode;
+ FinallyNode *fnode;
+} YYSTYPE;
+/* Line 1447 of yacc.c. */
+#line 193 "grammar.tab.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE kjsyylval;
+
+#if ! defined (YYLTYPE) && ! defined (YYLTYPE_IS_DECLARED)
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYLTYPE kjsyylloc;
+
+
diff --git a/kjs/grammar.y b/kjs/grammar.y
new file mode 100644
index 000000000..98294639d
--- /dev/null
+++ b/kjs/grammar.y
@@ -0,0 +1,699 @@
+%{
+
+/*
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "nodes.h"
+#include "lexer.h"
+#include "internal.h"
+
+/* default values for bison */
+#define YYDEBUG 0
+#ifdef YYMAXDEPTH
+#undef YYMAXDEPTH
+#endif
+#define YYERROR_VERBOSE
+#define DBG(l, s, e) { l->setLoc(s.first_line, e.last_line, Parser::source); } // location
+
+extern int yylex();
+static int yyerror (const char *);
+static bool automatic();
+
+using namespace KJS;
+
+%}
+
+%union {
+ int ival;
+ double dval;
+ UString *ustr;
+ Identifier *ident;
+ Node *node;
+ StatementNode *stat;
+ ParameterNode *param;
+ FunctionBodyNode *body;
+ FuncDeclNode *func;
+ FunctionBodyNode *prog;
+ AssignExprNode *init;
+ SourceElementsNode *srcs;
+ StatListNode *slist;
+ ArgumentsNode *args;
+ ArgumentListNode *alist;
+ VarDeclNode *decl;
+ VarDeclListNode *vlist;
+ CaseBlockNode *cblk;
+ ClauseListNode *clist;
+ CaseClauseNode *ccl;
+ ElementNode *elm;
+ Operator op;
+ PropertyValueNode *plist;
+ PropertyNode *pnode;
+ CatchNode *cnode;
+ FinallyNode *fnode;
+}
+
+%start Program
+
+/* expect a shift/reduce conflict from the "dangling else" problem
+ when using bison the warning can be supressed */
+// %expect 1
+
+/* literals */
+%token NULLTOKEN TRUETOKEN FALSETOKEN
+%token STRING NUMBER
+
+/* keywords */
+%token BREAK CASE DEFAULT FOR NEW VAR CONST CONTINUE
+%token FUNCTION RETURN VOID DELETE
+%token IF THIS DO WHILE ELSE IN INSTANCEOF TYPEOF
+%token SWITCH WITH RESERVED
+%token THROW TRY CATCH FINALLY
+%token DEBUGGER
+
+/* punctuators */
+%token EQEQ NE /* == and != */
+%token STREQ STRNEQ /* === and !== */
+%token LE GE /* < and > */
+%token OR AND /* || and && */
+%token PLUSPLUS MINUSMINUS /* ++ and -- */
+%token LSHIFT /* << */
+%token RSHIFT URSHIFT /* >> and >>> */
+%token PLUSEQUAL MINUSEQUAL /* += and -= */
+%token MULTEQUAL DIVEQUAL /* *= and /= */
+%token LSHIFTEQUAL /* <<= */
+%token RSHIFTEQUAL URSHIFTEQUAL /* >>= and >>>= */
+%token ANDEQUAL MODEQUAL /* &= and %= */
+%token XOREQUAL OREQUAL /* ^= and |= */
+
+/* terminal types */
+%token <dval> NUMBER
+%token <ustr> STRING
+%token <ident> IDENT FUNCEXPRIDENT
+
+/* automatically inserted semicolon */
+%token AUTOPLUSPLUS AUTOMINUSMINUS
+
+/* non-terminal types */
+%type <node> Literal PrimaryExpr Expr MemberExpr FunctionExpr NewExpr CallExpr
+%type <node> ArrayLiteral
+%type <node> LeftHandSideExpr PostfixExpr UnaryExpr
+%type <node> MultiplicativeExpr AdditiveExpr
+%type <node> ShiftExpr RelationalExpr EqualityExpr
+%type <node> BitwiseANDExpr BitwiseXORExpr BitwiseORExpr
+%type <node> LogicalANDExpr LogicalORExpr
+%type <node> ConditionalExpr AssignmentExpr
+%type <node> ExprOpt
+
+%type <cnode> Catch
+%type <fnode> Finally
+
+%type <stat> Statement Block
+%type <stat> VariableStatement ConstStatement EmptyStatement ExprStatement
+%type <stat> IfStatement IterationStatement ContinueStatement
+%type <stat> BreakStatement ReturnStatement WithStatement
+%type <stat> SwitchStatement LabelledStatement
+%type <stat> ThrowStatement TryStatement
+%type <stat> DebuggerStatement
+%type <stat> SourceElement
+
+%type <slist> StatementList
+%type <init> Initializer
+%type <func> FunctionDeclarationInternal
+%type <func> FunctionDeclaration
+%type <body> FunctionBody
+%type <srcs> SourceElements
+%type <param> FormalParameterList
+%type <op> AssignmentOperator
+%type <prog> Program
+%type <args> Arguments
+%type <alist> ArgumentList
+%type <vlist> VariableDeclarationList ConstDeclarationList
+%type <decl> VariableDeclaration ConstDeclaration
+%type <cblk> CaseBlock
+%type <ccl> CaseClause DefaultClause
+%type <clist> CaseClauses CaseClausesOpt
+%type <ival> Elision ElisionOpt
+%type <elm> ElementList
+%type <plist> PropertyNameAndValueList
+%type <pnode> PropertyName
+
+%%
+
+Literal:
+ NULLTOKEN { $$ = new NullNode(); }
+ | TRUETOKEN { $$ = new BooleanNode(true); }
+ | FALSETOKEN { $$ = new BooleanNode(false); }
+ | NUMBER { $$ = new NumberNode($1); }
+ | STRING { $$ = new StringNode($1); }
+ | '/' /* a RegExp ? */ { Lexer *l = Lexer::curr();
+ if (!l->scanRegExp()) YYABORT;
+ $$ = new RegExpNode(l->pattern,l->flags);}
+ | DIVEQUAL /* a RegExp starting with /= ! */
+ { Lexer *l = Lexer::curr();
+ if (!l->scanRegExp()) YYABORT;
+ $$ = new RegExpNode(UString('=')+l->pattern,l->flags);}
+;
+
+PrimaryExpr:
+ THIS { $$ = new ThisNode(); }
+ | IDENT { $$ = new ResolveNode(*$1); }
+ | Literal
+ | ArrayLiteral
+ | '(' Expr ')' { $$ = new GroupNode($2); }
+ | '{' '}' { $$ = new ObjectLiteralNode(); }
+ | '{' PropertyNameAndValueList '}' { $$ = new ObjectLiteralNode($2); }
+ | '{' PropertyNameAndValueList ',' '}' { $$ = new ObjectLiteralNode($2); }
+;
+
+ArrayLiteral:
+ '[' ElisionOpt ']' { $$ = new ArrayNode($2); }
+ | '[' ElementList ']' { $$ = new ArrayNode($2); }
+ | '[' ElementList ',' ElisionOpt ']' { $$ = new ArrayNode($4, $2); }
+;
+
+ElementList:
+ ElisionOpt AssignmentExpr { $$ = new ElementNode($1, $2); }
+ | ElementList ',' ElisionOpt AssignmentExpr
+ { $$ = new ElementNode($1, $3, $4); }
+;
+
+ElisionOpt:
+ /* nothing */ { $$ = 0; }
+ | Elision
+;
+
+Elision:
+ ',' { $$ = 1; }
+ | Elision ',' { $$ = $1 + 1; }
+;
+
+PropertyNameAndValueList:
+ PropertyName ':' AssignmentExpr { $$ = new PropertyValueNode($1, $3); }
+ | PropertyNameAndValueList ',' PropertyName ':' AssignmentExpr
+ { $$ = new PropertyValueNode($3, $5, $1); }
+;
+
+PropertyName:
+ IDENT { $$ = new PropertyNode(*$1); }
+ | STRING { $$ = new PropertyNode(Identifier(*$1)); }
+ | NUMBER { $$ = new PropertyNode($1); }
+;
+
+MemberExpr:
+ PrimaryExpr
+ | FunctionExpr
+ | MemberExpr '[' Expr ']' { $$ = new AccessorNode1($1, $3); }
+ | MemberExpr '.' IDENT { $$ = new AccessorNode2($1, *$3); }
+ | NEW MemberExpr Arguments { $$ = new NewExprNode($2, $3); }
+;
+
+NewExpr:
+ MemberExpr
+ | NEW NewExpr { $$ = new NewExprNode($2); }
+;
+
+CallExpr:
+ MemberExpr Arguments { $$ = new FunctionCallNode($1, $2); }
+ | CallExpr Arguments { $$ = new FunctionCallNode($1, $2); }
+ | CallExpr '[' Expr ']' { $$ = new AccessorNode1($1, $3); }
+ | CallExpr '.' IDENT { $$ = new AccessorNode2($1, *$3); }
+;
+
+Arguments:
+ '(' ')' { $$ = new ArgumentsNode(); }
+ | '(' ArgumentList ')' { $$ = new ArgumentsNode($2); }
+;
+
+ArgumentList:
+ AssignmentExpr { $$ = new ArgumentListNode($1); }
+ | ArgumentList ',' AssignmentExpr { $$ = new ArgumentListNode($1, $3); }
+;
+
+LeftHandSideExpr:
+ NewExpr
+ | CallExpr
+;
+
+PostfixExpr: /* TODO: no line terminator here */
+ LeftHandSideExpr
+ | LeftHandSideExpr PLUSPLUS { $$ = new PostfixNode($1, OpPlusPlus); }
+ | LeftHandSideExpr MINUSMINUS { $$ = new PostfixNode($1, OpMinusMinus); }
+;
+
+UnaryExpr:
+ PostfixExpr
+ | DELETE UnaryExpr { $$ = new DeleteNode($2); }
+ | VOID UnaryExpr { $$ = new VoidNode($2); }
+ | TYPEOF UnaryExpr { $$ = new TypeOfNode($2); }
+ | PLUSPLUS UnaryExpr { $$ = new PrefixNode(OpPlusPlus, $2); }
+ | AUTOPLUSPLUS UnaryExpr { $$ = new PrefixNode(OpPlusPlus, $2); }
+ | MINUSMINUS UnaryExpr { $$ = new PrefixNode(OpMinusMinus, $2); }
+ | AUTOMINUSMINUS UnaryExpr { $$ = new PrefixNode(OpMinusMinus, $2); }
+ | '+' UnaryExpr { $$ = new UnaryPlusNode($2); }
+ | '-' UnaryExpr { $$ = new NegateNode($2); }
+ | '~' UnaryExpr { $$ = new BitwiseNotNode($2); }
+ | '!' UnaryExpr { $$ = new LogicalNotNode($2); }
+;
+
+MultiplicativeExpr:
+ UnaryExpr
+ | MultiplicativeExpr '*' UnaryExpr { $$ = new MultNode($1, $3, '*'); }
+ | MultiplicativeExpr '/' UnaryExpr { $$ = new MultNode($1, $3, '/'); }
+ | MultiplicativeExpr '%' UnaryExpr { $$ = new MultNode($1,$3,'%'); }
+;
+
+AdditiveExpr:
+ MultiplicativeExpr
+ | AdditiveExpr '+' MultiplicativeExpr { $$ = AddNode::create($1, $3, '+'); }
+ | AdditiveExpr '-' MultiplicativeExpr { $$ = AddNode::create($1, $3, '-'); }
+;
+
+ShiftExpr:
+ AdditiveExpr
+ | ShiftExpr LSHIFT AdditiveExpr { $$ = new ShiftNode($1, OpLShift, $3); }
+ | ShiftExpr RSHIFT AdditiveExpr { $$ = new ShiftNode($1, OpRShift, $3); }
+ | ShiftExpr URSHIFT AdditiveExpr { $$ = new ShiftNode($1, OpURShift, $3); }
+;
+
+RelationalExpr:
+ ShiftExpr
+ | RelationalExpr '<' ShiftExpr
+ { $$ = new RelationalNode($1, OpLess, $3); }
+ | RelationalExpr '>' ShiftExpr
+ { $$ = new RelationalNode($1, OpGreater, $3); }
+ | RelationalExpr LE ShiftExpr
+ { $$ = new RelationalNode($1, OpLessEq, $3); }
+ | RelationalExpr GE ShiftExpr
+ { $$ = new RelationalNode($1, OpGreaterEq, $3); }
+ | RelationalExpr INSTANCEOF ShiftExpr
+ { $$ = new RelationalNode($1, OpInstanceOf, $3); }
+ | RelationalExpr IN ShiftExpr
+ { $$ = new RelationalNode($1, OpIn, $3); }
+;
+
+EqualityExpr:
+ RelationalExpr
+ | EqualityExpr EQEQ RelationalExpr { $$ = new EqualNode($1, OpEqEq, $3); }
+ | EqualityExpr NE RelationalExpr { $$ = new EqualNode($1, OpNotEq, $3); }
+ | EqualityExpr STREQ RelationalExpr { $$ = new EqualNode($1, OpStrEq, $3); }
+ | EqualityExpr STRNEQ RelationalExpr { $$ = new EqualNode($1, OpStrNEq, $3);}
+;
+
+BitwiseANDExpr:
+ EqualityExpr
+ | BitwiseANDExpr '&' EqualityExpr { $$ = new BitOperNode($1, OpBitAnd, $3); }
+;
+
+BitwiseXORExpr:
+ BitwiseANDExpr
+ | BitwiseXORExpr '^' BitwiseANDExpr { $$ = new BitOperNode($1, OpBitXOr, $3); }
+;
+
+BitwiseORExpr:
+ BitwiseXORExpr
+ | BitwiseORExpr '|' BitwiseXORExpr { $$ = new BitOperNode($1, OpBitOr, $3); }
+;
+
+LogicalANDExpr:
+ BitwiseORExpr
+ | LogicalANDExpr AND BitwiseORExpr
+ { $$ = new BinaryLogicalNode($1, OpAnd, $3); }
+;
+
+LogicalORExpr:
+ LogicalANDExpr
+ | LogicalORExpr OR LogicalANDExpr
+ { $$ = new BinaryLogicalNode($1, OpOr, $3); }
+;
+
+ConditionalExpr:
+ LogicalORExpr
+ | LogicalORExpr '?' AssignmentExpr ':' AssignmentExpr
+ { $$ = new ConditionalNode($1, $3, $5); }
+;
+
+AssignmentExpr:
+ ConditionalExpr
+ | LeftHandSideExpr AssignmentOperator AssignmentExpr
+ { $$ = new AssignNode($1, $2, $3);}
+;
+
+AssignmentOperator:
+ '=' { $$ = OpEqual; }
+ | PLUSEQUAL { $$ = OpPlusEq; }
+ | MINUSEQUAL { $$ = OpMinusEq; }
+ | MULTEQUAL { $$ = OpMultEq; }
+ | DIVEQUAL { $$ = OpDivEq; }
+ | LSHIFTEQUAL { $$ = OpLShift; }
+ | RSHIFTEQUAL { $$ = OpRShift; }
+ | URSHIFTEQUAL { $$ = OpURShift; }
+ | ANDEQUAL { $$ = OpAndEq; }
+ | XOREQUAL { $$ = OpXOrEq; }
+ | OREQUAL { $$ = OpOrEq; }
+ | MODEQUAL { $$ = OpModEq; }
+;
+
+Expr:
+ AssignmentExpr
+ | Expr ',' AssignmentExpr { $$ = new CommaNode($1, $3); }
+;
+
+Statement:
+ Block
+ | VariableStatement
+ | ConstStatement
+ | EmptyStatement
+ | ExprStatement
+ | IfStatement
+ | IterationStatement
+ | ContinueStatement
+ | BreakStatement
+ | ReturnStatement
+ | WithStatement
+ | SwitchStatement
+ | LabelledStatement
+ | ThrowStatement
+ | TryStatement
+ | DebuggerStatement
+;
+
+Block:
+ '{' '}' { $$ = new BlockNode(0); DBG($$, @2, @2); }
+ | '{' SourceElements '}' { $$ = new BlockNode($2); DBG($$, @3, @3); }
+;
+
+StatementList:
+ Statement { $$ = new StatListNode($1); }
+ | StatementList Statement { $$ = new StatListNode($1, $2); }
+;
+
+VariableStatement:
+ VAR VariableDeclarationList ';' { $$ = new VarStatementNode($2);
+ DBG($$, @1, @3); }
+ | VAR VariableDeclarationList error { if (automatic()) {
+ $$ = new VarStatementNode($2);
+ DBG($$, @1, @2);
+ } else {
+ YYABORT;
+ }
+ }
+;
+
+VariableDeclarationList:
+ VariableDeclaration { $$ = new VarDeclListNode($1); }
+ | VariableDeclarationList ',' VariableDeclaration
+ { $$ = new VarDeclListNode($1, $3); }
+;
+
+VariableDeclaration:
+ IDENT { $$ = new VarDeclNode(*$1, 0, VarDeclNode::Variable); }
+ | IDENT Initializer { $$ = new VarDeclNode(*$1, $2, VarDeclNode::Variable); }
+;
+
+ConstStatement:
+ CONST ConstDeclarationList ';' { $$ = new VarStatementNode($2);
+ DBG($$, @1, @3); }
+ | CONST ConstDeclarationList error { if (automatic()) {
+ $$ = new VarStatementNode($2);
+ DBG($$, @1, @2);
+ } else {
+ YYABORT;
+ }
+ }
+;
+
+ConstDeclarationList:
+ ConstDeclaration { $$ = new VarDeclListNode($1); }
+ | ConstDeclarationList ',' VariableDeclaration
+ { $$ = new VarDeclListNode($1, $3); }
+;
+
+ConstDeclaration:
+ IDENT { $$ = new VarDeclNode(*$1, 0, VarDeclNode::Constant); }
+ | IDENT Initializer { $$ = new VarDeclNode(*$1, $2, VarDeclNode::Constant); }
+;
+
+Initializer:
+ '=' AssignmentExpr { $$ = new AssignExprNode($2); }
+;
+
+EmptyStatement:
+ ';' { $$ = new EmptyStatementNode(); DBG($$, @1, @1); }
+;
+
+ExprStatement:
+ Expr ';' { $$ = new ExprStatementNode($1);
+ DBG($$, @1, @2); }
+ | Expr error { if (automatic()) {
+ $$ = new ExprStatementNode($1);
+ DBG($$, @1, @1);
+ } else
+ YYABORT; }
+;
+
+IfStatement: /* shift/reduce conflict due to dangling else */
+ IF '(' Expr ')' Statement { $$ = new IfNode($3,$5,0);DBG($$,@1,@4); }
+ | IF '(' Expr ')' Statement ELSE Statement
+ { $$ = new IfNode($3,$5,$7);DBG($$,@1,@4); }
+;
+
+IterationStatement:
+ DO Statement WHILE '(' Expr ')' { $$=new DoWhileNode($2,$5);DBG($$,@1,@3);}
+ | WHILE '(' Expr ')' Statement { $$ = new WhileNode($3,$5);DBG($$,@1,@4); }
+ | FOR '(' ExprOpt ';' ExprOpt ';' ExprOpt ')'
+ Statement { $$ = new ForNode($3,$5,$7,$9);
+ DBG($$,@1,@8); }
+ | FOR '(' VAR VariableDeclarationList ';' ExprOpt ';' ExprOpt ')'
+ Statement { $$ = new ForNode($4,$6,$8,$10);
+ DBG($$,@1,@9); }
+ | FOR '(' LeftHandSideExpr IN Expr ')'
+ Statement { $$ = new ForInNode($3, $5, $7);
+ DBG($$,@1,@6); }
+ | FOR '(' VAR IDENT IN Expr ')'
+ Statement { $$ = new ForInNode(*$4,0,$6,$8);
+ DBG($$,@1,@7); }
+ | FOR '(' VAR IDENT Initializer IN Expr ')'
+ Statement { $$ = new ForInNode(*$4,$5,$7,$9);
+ DBG($$,@1,@8); }
+;
+
+ExprOpt:
+ /* nothing */ { $$ = 0; }
+ | Expr
+;
+
+ContinueStatement:
+ CONTINUE ';' { $$ = new ContinueNode(); DBG($$,@1,@2); }
+ | CONTINUE error { if (automatic()) {
+ $$ = new ContinueNode(); DBG($$,@1,@2);
+ } else
+ YYABORT; }
+ | CONTINUE IDENT ';' { $$ = new ContinueNode(*$2); DBG($$,@1,@3); }
+ | CONTINUE IDENT error { if (automatic()) {
+ $$ = new ContinueNode(*$2);DBG($$,@1,@2);
+ } else
+ YYABORT; }
+;
+
+BreakStatement:
+ BREAK ';' { $$ = new BreakNode();DBG($$,@1,@2); }
+ | BREAK error { if (automatic()) {
+ $$ = new BreakNode(); DBG($$,@1,@1);
+ } else
+ YYABORT; }
+ | BREAK IDENT ';' { $$ = new BreakNode(*$2); DBG($$,@1,@3); }
+ | BREAK IDENT error { if (automatic()) {
+ $$ = new BreakNode(*$2); DBG($$,@1,@2);
+ } else
+ YYABORT;
+ }
+;
+
+ReturnStatement:
+ RETURN ';' { $$ = new ReturnNode(0); DBG($$,@1,@2); }
+ | RETURN error { if (automatic()) {
+ $$ = new ReturnNode(0); DBG($$,@1,@1);
+ } else
+ YYABORT; }
+ | RETURN Expr ';' { $$ = new ReturnNode($2); DBG($$,@1,@3); }
+ | RETURN Expr error { if (automatic()) {
+ $$ = new ReturnNode($2); DBG($$,@1,@1);
+ }
+ else
+ YYABORT; }
+;
+
+WithStatement:
+ WITH '(' Expr ')' Statement { $$ = new WithNode($3,$5);
+ DBG($$, @1, @4); }
+;
+
+SwitchStatement:
+ SWITCH '(' Expr ')' CaseBlock { $$ = new SwitchNode($3, $5);
+ DBG($$, @1, @4); }
+;
+
+CaseBlock:
+ '{' CaseClausesOpt '}' { $$ = new CaseBlockNode($2, 0, 0); }
+ | '{' CaseClausesOpt DefaultClause CaseClausesOpt '}'
+ { $$ = new CaseBlockNode($2, $3, $4); }
+;
+
+CaseClausesOpt:
+ /* nothing */ { $$ = 0; }
+ | CaseClauses
+;
+
+CaseClauses:
+ CaseClause { $$ = new ClauseListNode($1); }
+ | CaseClauses CaseClause { $$ = new ClauseListNode($1, $2); }
+;
+
+CaseClause:
+ CASE Expr ':' { $$ = new CaseClauseNode($2); }
+ | CASE Expr ':' StatementList { $$ = new CaseClauseNode($2, $4); }
+;
+
+DefaultClause:
+ DEFAULT ':' { $$ = new CaseClauseNode(0); }
+ | DEFAULT ':' StatementList { $$ = new CaseClauseNode(0, $3); }
+;
+
+LabelledStatement:
+ IDENT ':' Statement { $3->pushLabel(*$1);
+ $$ = new LabelNode(*$1, $3); DBG($$,@1,@2); }
+;
+
+ThrowStatement:
+ THROW Expr ';' { $$ = new ThrowNode($2); DBG($$,@1,@3); }
+ | THROW Expr error { if (automatic()) {
+ $$ = new ThrowNode($2); DBG($$,@1,@1);
+ } else {
+ YYABORT; } }
+;
+
+TryStatement:
+ TRY Block Catch { $$ = new TryNode($2, $3); DBG($$,@1,@1); }
+ | TRY Block Finally { $$ = new TryNode($2, $3); DBG($$,@1,@1); }
+ | TRY Block Catch Finally { $$ = new TryNode($2, $3, $4); DBG($$,@1,@1); }
+;
+
+DebuggerStatement:
+ DEBUGGER ';' { $$ = new EmptyStatementNode(); DBG($$, @1, @2); }
+ | DEBUGGER error { if (automatic()) {
+ $$ = new EmptyStatementNode();
+ DBG($$, @1, @1);
+ } else {
+ YYABORT; } }
+;
+
+
+Catch:
+ CATCH '(' IDENT ')' Block { CatchNode *c; $$ = c = new CatchNode(*$3, $5);
+ DBG(c,@1,@4); }
+;
+
+Finally:
+ FINALLY Block { FinallyNode *f; $$ = f = new FinallyNode($2); DBG(f,@1,@1); }
+;
+
+FunctionDeclaration:
+ FunctionDeclarationInternal
+ /* Hack for IE/NS4 compatibility */
+ | VOID FunctionDeclarationInternal { $$ = $2; }
+;
+
+FunctionDeclarationInternal:
+ FUNCTION IDENT '(' ')' FunctionBody { $$ = new FuncDeclNode(*$2, $5); DBG($$,@1,@4); }
+ | FUNCTION IDENT '(' FormalParameterList ')' FunctionBody
+ { $$ = new FuncDeclNode(*$2, $4, $6); DBG($$,@1,@5); }
+;
+
+FunctionExpr:
+ FUNCTION '(' ')' FunctionBody
+ { $$ = new FuncExprNode(Identifier::null(), $4); }
+ | FUNCTION '(' FormalParameterList ')' FunctionBody
+ { $$ = new FuncExprNode(Identifier::null(), $3, $5); }
+ | FUNCTION FUNCEXPRIDENT '(' ')' FunctionBody
+ { $$ = new FuncExprNode(*$2, $5); }
+ | FUNCTION FUNCEXPRIDENT '(' FormalParameterList ')' FunctionBody
+ { $$ = new FuncExprNode(*$2, $4, $6); }
+;
+
+FormalParameterList:
+ IDENT { $$ = new ParameterNode(*$1); }
+ | FormalParameterList ',' IDENT { $$ = new ParameterNode($1, *$3); }
+;
+
+FunctionBody:
+ '{' '}' /* TODO: spec ??? */ { $$ = new FunctionBodyNode(0);
+ DBG($$, @1, @2);}
+ | '{' SourceElements '}' { $$ = new FunctionBodyNode($2);
+ DBG($$, @1, @3);}
+;
+
+Program:
+ /* nothing, empty script */ { $$ = new FunctionBodyNode(0);
+ $$->setLoc(0, 0, Parser::source);
+ Parser::progNode = $$; }
+ | SourceElements { $$ = new FunctionBodyNode($1);
+ Parser::progNode = $$; }
+;
+
+SourceElements:
+ SourceElement { $$ = new SourceElementsNode($1); }
+ | SourceElements SourceElement { $$ = new SourceElementsNode($1, $2); }
+;
+
+SourceElement:
+ Statement { $$ = $1; }
+ | FunctionDeclaration { $$ = $1; }
+;
+
+%%
+
+int yyerror (const char * /* s */) /* Called by yyparse on error */
+{
+ // fprintf(stderr, "ERROR: %s at line %d\n",
+ // s, KJS::Lexer::curr()->lineNo());
+ return 1;
+}
+
+/* may we automatically insert a semicolon ? */
+bool automatic()
+{
+ if (Lexer::curr()->hadError())
+ return false;
+ if (yychar == '}' || yychar == 0)
+ return true;
+ else if (Lexer::curr()->prevTerminator())
+ return true;
+
+ return false;
+}
diff --git a/kjs/identifier.cpp b/kjs/identifier.cpp
new file mode 100644
index 000000000..835ab1208
--- /dev/null
+++ b/kjs/identifier.cpp
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "identifier.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DUMP_STATISTICS 0
+
+namespace KJS {
+
+#if DUMP_STATISTICS
+
+static int numProbes;
+static int numCollisions;
+
+struct IdentifierStatisticsExitLogger { ~IdentifierStatisticsExitLogger(); };
+
+static IdentifierStatisticsExitLogger logger;
+
+IdentifierStatisticsExitLogger::~IdentifierStatisticsExitLogger()
+{
+ printf("\nKJS::Identifier statistics\n\n");
+ printf("%d probes\n", numProbes);
+ printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
+}
+
+#endif
+
+extern const Identifier argumentsPropertyName("arguments");
+extern const Identifier calleePropertyName("callee");
+extern const Identifier callerPropertyName("caller");
+extern const Identifier constructorPropertyName("constructor");
+extern const Identifier lengthPropertyName("length");
+extern const Identifier messagePropertyName("message");
+extern const Identifier namePropertyName("name");
+extern const Identifier prototypePropertyName("prototype");
+extern const Identifier specialPrototypePropertyName("__proto__");
+extern const Identifier toLocaleStringPropertyName("toLocaleString");
+extern const Identifier toStringPropertyName("toString");
+extern const Identifier valueOfPropertyName("valueOf");
+
+static const int _minTableSize = 64;
+
+UString::Rep **Identifier::_table;
+int Identifier::_tableSize;
+int Identifier::_tableSizeMask;
+int Identifier::_keyCount;
+
+bool Identifier::equal(UString::Rep *r, const char *s)
+{
+ int length = r->len;
+ const UChar *d = r->data();
+ for (int i = 0; i != length; ++i)
+ if (d[i].uc != (unsigned char)s[i])
+ return false;
+ return s[length] == 0;
+}
+
+bool Identifier::equal(UString::Rep *r, const UChar *s, int length)
+{
+ if (r->len != length)
+ return false;
+ const UChar *d = r->data();
+ for (int i = 0; i != length; ++i)
+ if (d[i].uc != s[i].uc)
+ return false;
+ return true;
+}
+
+bool Identifier::equal(UString::Rep *r, UString::Rep *b)
+{
+ int length = r->len;
+ if (length != b->len)
+ return false;
+ const UChar *d = r->data();
+ const UChar *s = b->data();
+ for (int i = 0; i != length; ++i)
+ if (d[i].uc != s[i].uc)
+ return false;
+ return true;
+}
+
+UString::Rep *Identifier::add(const char *c)
+{
+ if (!c)
+ return &UString::Rep::null;
+ int length = strlen(c);
+ if (length == 0)
+ return &UString::Rep::empty;
+
+ if (!_table)
+ expand();
+
+ unsigned hash = UString::Rep::computeHash(c);
+
+ int i = hash & _tableSizeMask;
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table[i] && !equal(_table[i], c);
+#endif
+ while (UString::Rep *key = _table[i]) {
+ if (equal(key, c))
+ return key;
+ i = (i + 1) & _tableSizeMask;
+ }
+
+ UChar *d = new UChar[length];
+ for (int j = 0; j != length; j++)
+ d[j] = c[j];
+
+ UString::Rep *r = new UString::Rep;
+ r->dat = d;
+ r->len = length;
+ r->capacity = UString::Rep::capacityForIdentifier;
+ r->rc = 0;
+ r->_hash = hash;
+
+ _table[i] = r;
+ ++_keyCount;
+
+ if (_keyCount * 2 >= _tableSize)
+ expand();
+
+ return r;
+}
+
+UString::Rep *Identifier::add(const UChar *s, int length)
+{
+ if (length == 0)
+ return &UString::Rep::empty;
+
+ if (!_table)
+ expand();
+
+ unsigned hash = UString::Rep::computeHash(s, length);
+
+ int i = hash & _tableSizeMask;
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table[i] && !equal(_table[i], s, length);
+#endif
+ while (UString::Rep *key = _table[i]) {
+ if (equal(key, s, length))
+ return key;
+ i = (i + 1) & _tableSizeMask;
+ }
+
+ UChar *d = new UChar[length];
+ for (int j = 0; j != length; j++)
+ d[j] = s[j];
+
+ UString::Rep *r = new UString::Rep;
+ r->dat = d;
+ r->len = length;
+ r->capacity = UString::Rep::capacityForIdentifier;
+ r->rc = 0;
+ r->_hash = hash;
+
+ _table[i] = r;
+ ++_keyCount;
+
+ if (_keyCount * 2 >= _tableSize)
+ expand();
+
+ return r;
+}
+
+UString::Rep *Identifier::add(UString::Rep *r)
+{
+ if (r->capacity == UString::Rep::capacityForIdentifier)
+ return r;
+ if (r->len == 0)
+ return &UString::Rep::empty;
+
+ if (!_table)
+ expand();
+
+ unsigned hash = r->hash();
+
+ int i = hash & _tableSizeMask;
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table[i] && !equal(_table[i], r);
+#endif
+ while (UString::Rep *key = _table[i]) {
+ if (equal(key, r))
+ return key;
+ i = (i + 1) & _tableSizeMask;
+ }
+
+ r->capacity = UString::Rep::capacityForIdentifier;
+
+ _table[i] = r;
+ ++_keyCount;
+
+ if (_keyCount * 2 >= _tableSize)
+ expand();
+
+ return r;
+}
+
+inline void Identifier::insert(UString::Rep *key)
+{
+ unsigned hash = key->hash();
+
+ int i = hash & _tableSizeMask;
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table[i] != 0;
+#endif
+ while (_table[i])
+ i = (i + 1) & _tableSizeMask;
+
+ _table[i] = key;
+}
+
+void Identifier::remove(UString::Rep *r)
+{
+ unsigned hash = r->hash();
+
+ UString::Rep *key;
+
+ int i = hash & _tableSizeMask;
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table[i] && equal(_table[i], r);
+#endif
+ while ((key = _table[i])) {
+ if (equal(key, r))
+ break;
+ i = (i + 1) & _tableSizeMask;
+ }
+ if (!key)
+ return;
+
+ _table[i] = 0;
+ --_keyCount;
+
+ if (_keyCount * 6 < _tableSize && _tableSize > _minTableSize) {
+ shrink();
+ return;
+ }
+
+ // Reinsert all the items to the right in the same cluster.
+ while (1) {
+ i = (i + 1) & _tableSizeMask;
+ key = _table[i];
+ if (!key)
+ break;
+ _table[i] = 0;
+ insert(key);
+ }
+}
+
+void Identifier::expand()
+{
+ rehash(_tableSize == 0 ? _minTableSize : _tableSize * 2);
+}
+
+void Identifier::shrink()
+{
+ rehash(_tableSize / 2);
+}
+
+void Identifier::rehash(int newTableSize)
+{
+ int oldTableSize = _tableSize;
+ UString::Rep **oldTable = _table;
+
+ _tableSize = newTableSize;
+ _tableSizeMask = newTableSize - 1;
+ _table = (UString::Rep **)calloc(newTableSize, sizeof(UString::Rep *));
+
+ for (int i = 0; i != oldTableSize; ++i)
+ if (UString::Rep *key = oldTable[i])
+ insert(key);
+
+ free(oldTable);
+}
+
+const Identifier &Identifier::null()
+{
+ static Identifier null;
+ return null;
+}
+
+} // namespace KJS
diff --git a/kjs/identifier.h b/kjs/identifier.h
new file mode 100644
index 000000000..5867077d8
--- /dev/null
+++ b/kjs/identifier.h
@@ -0,0 +1,155 @@
+/*
+ * 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_IDENTIFIER_H
+#define KJS_IDENTIFIER_H
+
+#include "ustring.h"
+
+namespace KJS {
+
+ /**
+ * Represents an Identifier for a Javascript object.
+ */
+ class KJS_EXPORT Identifier {
+ friend class PropertyMap;
+ public:
+ /**
+ * Creates an empty identifier
+ */
+ Identifier() { }
+ /**
+ * Creates an identifier with the name of the string
+ * @code
+ * KJS::Identifier method("someJSMethod");
+ * @endcode
+ */
+ Identifier(const char *s) : _ustring(add(s)) { }
+ Identifier(const UChar *s, int length) : _ustring(add(s, length)) { }
+ explicit Identifier(const UString &s) : _ustring(add(s.rep)) { }
+
+ /**
+ * returns a UString of the identifier
+ */
+ const UString &ustring() const { return _ustring; }
+ DOM::DOMString string() const;
+ /**
+ * returns a QString of the identifier
+ */
+ QString qstring() const;
+
+ /**
+ * returns a UChar pointer to the string of the identifier with a size defined by @ref size().
+ */
+ const UChar *data() const { return _ustring.data(); }
+ /**
+ * The size of the UChar string returned.
+ */
+ int size() const { return _ustring.size(); }
+
+ /**
+ * Char * of the identifier's string.
+ */
+ const char *ascii() const { return _ustring.ascii(); }
+
+ static Identifier from(unsigned y) { return Identifier(UString::from(y)); }
+
+ /**
+ * Returns the identfiers state of being unset.
+ */
+ bool isNull() const { return _ustring.isNull(); }
+ /**
+ * Returns that the identifiers string is set, but is empty.
+ */
+ bool isEmpty() const { return _ustring.isEmpty(); }
+
+ unsigned long toULong(bool *ok) const { return _ustring.toULong(ok); }
+ unsigned toStrictUInt32(bool *ok) const { return _ustring.toStrictUInt32(ok); }
+ unsigned toArrayIndex(bool *ok) const { return _ustring.toArrayIndex(ok); }
+
+ double toDouble() const { return _ustring.toDouble(); }
+
+ /**
+ * Creates an empty Identifier
+ */
+ static const Identifier &null();
+
+ friend bool operator==(const Identifier &, const Identifier &);
+ friend bool operator!=(const Identifier &, const Identifier &);
+
+ friend bool operator==(const Identifier &, const char *);
+
+ static void remove(UString::Rep *);
+
+ private:
+ UString _ustring;
+
+ static bool equal(UString::Rep *, const char *);
+ static bool equal(UString::Rep *, const UChar *, int length);
+ static bool equal(UString::Rep *, UString::Rep *);
+
+ 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 UString::Rep *add(const char *);
+ static UString::Rep *add(const UChar *, int length);
+ static UString::Rep *add(UString::Rep *);
+
+ static void insert(UString::Rep *);
+
+ static void rehash(int newTableSize);
+ static void expand();
+ static void shrink();
+
+ // TODO: move into .cpp file
+ static UString::Rep **_table;
+ static int _tableSize;
+ static int _tableSizeMask;
+ static int _keyCount;
+ };
+
+ inline bool operator==(const Identifier &a, const Identifier &b)
+ { return Identifier::equal(a, b); }
+
+ inline bool operator!=(const Identifier &a, const Identifier &b)
+ { return !Identifier::equal(a, b); }
+
+ inline bool operator==(const Identifier &a, const char *b)
+ { return Identifier::equal(a, b); }
+
+ KJS_EXPORT extern const Identifier argumentsPropertyName;
+ KJS_EXPORT extern const Identifier calleePropertyName;
+ KJS_EXPORT extern const Identifier callerPropertyName;
+ KJS_EXPORT extern const Identifier constructorPropertyName;
+ KJS_EXPORT extern const Identifier lengthPropertyName;
+ KJS_EXPORT extern const Identifier messagePropertyName;
+ KJS_EXPORT extern const Identifier namePropertyName;
+ KJS_EXPORT extern const Identifier prototypePropertyName;
+ KJS_EXPORT extern const Identifier specialPrototypePropertyName;
+ KJS_EXPORT extern const Identifier toLocaleStringPropertyName;
+ KJS_EXPORT extern const Identifier toStringPropertyName;
+ KJS_EXPORT extern const Identifier valueOfPropertyName;
+
+}
+
+#endif
diff --git a/kjs/internal.cpp b/kjs/internal.cpp
new file mode 100644
index 000000000..25f7b6e31
--- /dev/null
+++ b/kjs/internal.cpp
@@ -0,0 +1,1088 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2004 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+#include "array_object.h"
+#include "bool_object.h"
+#include "collector.h"
+#include "context.h"
+#include "date_object.h"
+#include "debugger.h"
+#include "error_object.h"
+#include "function_object.h"
+#include "internal.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"
+
+#define I18N_NOOP(s) s
+
+extern int kjsyyparse();
+
+using namespace KJS;
+
+namespace KJS {
+ /* work around some strict alignment requirements
+ for double variables on some architectures (e.g. PA-RISC) */
+ typedef union { unsigned char b[8]; double d; } kjs_double_t;
+
+#ifdef WORDS_BIGENDIAN
+ static const kjs_double_t NaN_Bytes = { { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 } };
+ static const kjs_double_t Inf_Bytes = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } };
+#elif defined(arm)
+ static const kjs_double_t NaN_Bytes = { { 0, 0, 0xf8, 0x7f, 0, 0, 0, 0 } };
+ static const kjs_double_t Inf_Bytes = { { 0, 0, 0xf0, 0x7f, 0, 0, 0, 0 } };
+#else
+ static const kjs_double_t NaN_Bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } };
+ static const kjs_double_t Inf_Bytes = { { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } };
+#endif
+
+ const double NaN = NaN_Bytes.d;
+ const double Inf = Inf_Bytes.d;
+}
+
+#ifdef KJS_THREADSUPPORT
+static pthread_once_t interpreterLockOnce = PTHREAD_ONCE_INIT;
+static pthread_mutex_t interpreterLock;
+static int interpreterLockCount = 0;
+
+static void initializeInterpreterLock()
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
+
+ pthread_mutex_init(&interpreterLock, &attr);
+}
+#endif
+
+static inline void lockInterpreter()
+{
+#ifdef KJS_THREADSUPPORT
+ pthread_once(&interpreterLockOnce, initializeInterpreterLock);
+ pthread_mutex_lock(&interpreterLock);
+ interpreterLockCount++;
+#endif
+}
+
+static inline void unlockInterpreter()
+{
+#ifdef KJS_THREADSUPPORT
+ interpreterLockCount--;
+ pthread_mutex_unlock(&interpreterLock);
+#endif
+}
+
+
+
+// ------------------------------ UndefinedImp ---------------------------------
+
+UndefinedImp *UndefinedImp::staticUndefined = 0;
+
+Value UndefinedImp::toPrimitive(ExecState* /*exec*/, Type) const
+{
+ return Value((ValueImp*)this);
+}
+
+bool UndefinedImp::toBoolean(ExecState* /*exec*/) const
+{
+ return false;
+}
+
+double UndefinedImp::toNumber(ExecState* /*exec*/) const
+{
+ return NaN;
+}
+
+UString UndefinedImp::toString(ExecState* /*exec*/) const
+{
+ return "undefined";
+}
+
+Object UndefinedImp::toObject(ExecState *exec) const
+{
+ Object err = Error::create(exec, TypeError, I18N_NOOP("Undefined value"));
+ exec->setException(err);
+ return err;
+}
+
+// ------------------------------ NullImp --------------------------------------
+
+NullImp *NullImp::staticNull = 0;
+
+Value NullImp::toPrimitive(ExecState* /*exec*/, Type) const
+{
+ return Value((ValueImp*)this);
+}
+
+bool NullImp::toBoolean(ExecState* /*exec*/) const
+{
+ return false;
+}
+
+double NullImp::toNumber(ExecState* /*exec*/) const
+{
+ return 0.0;
+}
+
+UString NullImp::toString(ExecState* /*exec*/) const
+{
+ return "null";
+}
+
+Object NullImp::toObject(ExecState *exec) const
+{
+ Object err = Error::create(exec, TypeError, I18N_NOOP("Null value"));
+ exec->setException(err);
+ return err;
+}
+
+// ------------------------------ BooleanImp -----------------------------------
+
+BooleanImp* BooleanImp::staticTrue = 0;
+BooleanImp* BooleanImp::staticFalse = 0;
+
+Value BooleanImp::toPrimitive(ExecState* /*exec*/, Type) const
+{
+ return Value((ValueImp*)this);
+}
+
+bool BooleanImp::toBoolean(ExecState* /*exec*/) const
+{
+ return val;
+}
+
+double BooleanImp::toNumber(ExecState* /*exec*/) const
+{
+ return val ? 1.0 : 0.0;
+}
+
+UString BooleanImp::toString(ExecState* /*exec*/) const
+{
+ return val ? "true" : "false";
+}
+
+Object BooleanImp::toObject(ExecState *exec) const
+{
+ List args;
+ args.append(const_cast<BooleanImp*>(this));
+ return Object::dynamicCast(exec->lexicalInterpreter()->builtinBoolean().construct(exec,args));
+}
+
+// ------------------------------ StringImp ------------------------------------
+
+Value StringImp::toPrimitive(ExecState* /*exec*/, Type) const
+{
+ return Value((ValueImp*)this);
+}
+
+bool StringImp::toBoolean(ExecState* /*exec*/) const
+{
+ return (val.size() > 0);
+}
+
+double StringImp::toNumber(ExecState* /*exec*/) const
+{
+ return val.toDouble();
+}
+
+UString StringImp::toString(ExecState* /*exec*/) const
+{
+ return val;
+}
+
+Object StringImp::toObject(ExecState *exec) const
+{
+ List args;
+ args.append(const_cast<StringImp*>(this));
+ return Object(static_cast<ObjectImp *>(exec->lexicalInterpreter()->builtinString().construct(exec, args).imp()));
+}
+
+// ------------------------------ NumberImp ------------------------------------
+
+NumberImp *NumberImp::staticNaN;
+
+ValueImp *NumberImp::create(int i)
+{
+ if (SimpleNumber::fits(i))
+ return SimpleNumber::make(i);
+ NumberImp *imp = new NumberImp(static_cast<double>(i));
+ imp->setGcAllowedFast();
+ return imp;
+}
+
+ValueImp *NumberImp::create(double d)
+{
+ if (SimpleNumber::fits(d))
+ return SimpleNumber::make((int)d);
+ if (isNaN(d))
+ return staticNaN;
+ NumberImp *imp = new NumberImp(d);
+ imp->setGcAllowedFast();
+ return imp;
+}
+
+Value NumberImp::toPrimitive(ExecState *, Type) const
+{
+ return Number((NumberImp*)this);
+}
+
+bool NumberImp::toBoolean(ExecState *) const
+{
+ return !((val == 0) /* || (iVal() == N0) */ || isNaN(val));
+}
+
+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);
+}
+
+Object NumberImp::toObject(ExecState *exec) const
+{
+ List args;
+ args.append(const_cast<NumberImp*>(this));
+ return Object::dynamicCast(exec->lexicalInterpreter()->builtinNumber().construct(exec,args));
+}
+
+bool NumberImp::toUInt32(unsigned& uint32) const
+{
+ uint32 = (unsigned)val;
+ return (double)uint32 == val;
+}
+
+double SimpleNumber::negZero = -0.0;
+
+// ------------------------------ LabelStack -----------------------------------
+
+LabelStack::LabelStack(const LabelStack &other)
+{
+ tos = 0;
+ *this = other;
+}
+
+LabelStack &LabelStack::operator=(const LabelStack &other)
+{
+ clear();
+ tos = 0;
+ StackElem *cur = 0;
+ StackElem *se = other.tos;
+ while (se) {
+ StackElem *newPrev = new StackElem;
+ newPrev->prev = 0;
+ newPrev->id = se->id;
+ if (cur)
+ cur->prev = newPrev;
+ else
+ tos = newPrev;
+ cur = newPrev;
+ se = se->prev;
+ }
+ return *this;
+}
+
+bool LabelStack::push(const Identifier &id)
+{
+ if (id.isEmpty() || 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;
+}
+
+void LabelStack::pop()
+{
+ if (tos) {
+ StackElem *prev = tos->prev;
+ delete tos;
+ tos = prev;
+ }
+}
+
+LabelStack::~LabelStack()
+{
+ clear();
+}
+
+void LabelStack::clear()
+{
+ StackElem *prev;
+
+ while (tos) {
+ prev = tos->prev;
+ delete tos;
+ tos = prev;
+ }
+}
+
+// ------------------------------ ContextImp -----------------------------------
+
+
+// ECMA 10.2
+ContextImp::ContextImp(Object &glob, InterpreterImp *interpreter, Object &thisV, int _sourceId, CodeType type,
+ ContextImp *callingCon, FunctionImp *func, const List *args)
+ : _interpreter(interpreter), _function(func), _arguments(args)
+{
+ m_codeType = type;
+ _callingContext = callingCon;
+ tryCatch = 0;
+
+ sourceId = _sourceId;
+ line0 = 1;
+ line1 = 1;
+
+ if (func && func->inherits(&DeclaredFunctionImp::info))
+ functionName = static_cast<DeclaredFunctionImp*>(func)->name();
+ else
+ functionName = Identifier::null();
+
+ // create and initialize activation object (ECMA 10.1.6)
+ if (type == FunctionCode) {
+ activation = Object(new ActivationImp(func,*args));
+ variable = activation;
+ } else {
+ activation = Object();
+ variable = glob;
+ }
+
+ // ECMA 10.2
+ switch(type) {
+ case EvalCode:
+ if (_callingContext) {
+ scope = _callingContext->scopeChain();
+#ifndef KJS_PURE_ECMA
+ if (thisV.imp() != glob.imp())
+ scope.push(thisV.imp()); // for deprecated Object.prototype.eval()
+#endif
+ variable = _callingContext->variableObject();
+ thisVal = _callingContext->thisValue();
+ break;
+ } // else same as GlobalCode
+ case GlobalCode:
+ scope.clear();
+ scope.push(glob.imp());
+#ifndef KJS_PURE_ECMA
+ if (thisV.isValid())
+ thisVal = thisV;
+ else
+#endif
+ thisVal = glob;
+ break;
+ case FunctionCode:
+ scope = func->scope();
+ scope.push(activation.imp());
+ variable = activation; // TODO: DontDelete ? (ECMA 10.2.3)
+ thisVal = thisV;
+ break;
+ }
+
+ _interpreter->setContext(this);
+}
+
+ContextImp::~ContextImp()
+{
+ _interpreter->setContext(_callingContext);
+}
+
+void ContextImp::mark()
+{
+ for (ContextImp *context = this; context; context = context->_callingContext) {
+ context->scope.mark();
+ }
+}
+
+bool ContextImp::inTryCatch() const
+{
+ const ContextImp *c = this;
+ while (c && !c->tryCatch)
+ c = c->_callingContext;
+ return (c && c->tryCatch);
+}
+
+// ---------------------------- SourceCode -------------------------------------
+
+void SourceCode::cleanup()
+{
+ if (interpreter && interpreter->debugger())
+ interpreter->debugger()->sourceUnused(interpreter->globalExec(),sid);
+ if (interpreter)
+ interpreter->removeSourceCode(this);
+ delete this;
+}
+
+// ------------------------------ Parser ---------------------------------------
+
+FunctionBodyNode *Parser::progNode = 0;
+int Parser::sid = 0;
+SourceCode *Parser::source = 0;
+
+FunctionBodyNode *Parser::parse(const UChar *code, unsigned int length, SourceCode **src,
+ int *errLine, UString *errMsg)
+{
+ if (errLine)
+ *errLine = -1;
+ if (errMsg)
+ *errMsg = 0;
+
+ Lexer::curr()->setCode(code, length);
+ progNode = 0;
+ sid++;
+
+ source = new SourceCode(sid);
+ source->ref();
+ *src = source;
+
+ // Enable this (and the #define YYDEBUG in grammar.y) to debug a parse error
+ //extern int kjsyydebug;
+ //kjsyydebug=1;
+ int parseError = kjsyyparse();
+ if (Lexer::curr()->hadError())
+ parseError = 1;
+ Lexer::curr()->doneParsing();
+ FunctionBodyNode *prog = progNode;
+ progNode = 0;
+ //sid = -1;
+ source = 0;
+
+ if (parseError) {
+ int eline = Lexer::curr()->lineNo();
+ if (errLine)
+ *errLine = eline;
+ if (errMsg)
+ *errMsg = "Parse error at line " + UString::from(eline);
+#ifdef KJS_VERBOSE
+ fprintf( stderr, "%s\n", UString(code,length).ascii() );
+#endif
+#ifndef NDEBUG
+ fprintf(stderr, "KJS: JavaScript parse error at line %d.\n", eline);
+#endif
+ delete prog;
+ return 0;
+ }
+#ifdef KJS_VERBOSE
+ fprintf( stderr, "%s\n", prog->toCode().ascii() );
+#endif
+
+ return prog;
+}
+
+// ------------------------------ InterpreterImp -------------------------------
+
+InterpreterImp* InterpreterImp::s_hook = 0L;
+
+void InterpreterImp::globalInit()
+{
+ //fprintf( stderr, "InterpreterImp::globalInit()\n" );
+ UndefinedImp::staticUndefined = new UndefinedImp();
+ UndefinedImp::staticUndefined->ref();
+ NullImp::staticNull = new NullImp();
+ NullImp::staticNull->ref();
+ BooleanImp::staticTrue = new BooleanImp(true);
+ BooleanImp::staticTrue->ref();
+ BooleanImp::staticFalse = new BooleanImp(false);
+ BooleanImp::staticFalse->ref();
+ NumberImp::staticNaN = new NumberImp(NaN);
+ NumberImp::staticNaN->ref();
+}
+
+void InterpreterImp::globalClear()
+{
+ //fprintf( stderr, "InterpreterImp::globalClear()\n" );
+ UndefinedImp::staticUndefined->deref();
+ UndefinedImp::staticUndefined->setGcAllowed();
+ UndefinedImp::staticUndefined = 0L;
+ NullImp::staticNull->deref();
+ NullImp::staticNull->setGcAllowed();
+ NullImp::staticNull = 0L;
+ BooleanImp::staticTrue->deref();
+ BooleanImp::staticTrue->setGcAllowed();
+ BooleanImp::staticTrue = 0L;
+ BooleanImp::staticFalse->deref();
+ BooleanImp::staticFalse->setGcAllowed();
+ BooleanImp::staticFalse = 0L;
+ NumberImp::staticNaN->deref();
+ NumberImp::staticNaN->setGcAllowed();
+ NumberImp::staticNaN = 0;
+}
+
+InterpreterImp::InterpreterImp(Interpreter *interp, const Object &glob)
+ : m_interpreter(interp),
+ global(glob),
+ dbg(0),
+ m_compatMode(Interpreter::NativeMode),
+ _context(0),
+ recursion(0),
+ sources(0)
+{
+ // add this interpreter to the global chain
+ // as a root set for garbage collection
+ lockInterpreter();
+ if (s_hook) {
+ prev = s_hook;
+ next = s_hook->next;
+ s_hook->next->prev = this;
+ s_hook->next = this;
+ } else {
+ // This is the first interpreter
+ s_hook = next = prev = this;
+ globalInit();
+ }
+ unlockInterpreter();
+
+ globExec = new ExecState(m_interpreter,0);
+
+ // initialize properties of the global object
+ initGlobalObject();
+}
+
+void InterpreterImp::lock()
+{
+ lockInterpreter();
+}
+
+void InterpreterImp::unlock()
+{
+ unlockInterpreter();
+}
+
+void InterpreterImp::initGlobalObject()
+{
+ // Contructor prototype objects (Object.prototype, Array.prototype etc)
+
+ FunctionPrototypeImp *funcProto = new FunctionPrototypeImp(globExec);
+ b_FunctionPrototype = Object(funcProto);
+ ObjectPrototypeImp *objProto = new ObjectPrototypeImp(globExec,funcProto);
+ b_ObjectPrototype = Object(objProto);
+ funcProto->setPrototype(b_ObjectPrototype);
+
+ ArrayPrototypeImp *arrayProto = new ArrayPrototypeImp(globExec,objProto);
+ b_ArrayPrototype = Object(arrayProto);
+ StringPrototypeImp *stringProto = new StringPrototypeImp(globExec,objProto);
+ b_StringPrototype = Object(stringProto);
+ BooleanPrototypeImp *booleanProto = new BooleanPrototypeImp(globExec,objProto,funcProto);
+ b_BooleanPrototype = Object(booleanProto);
+ NumberPrototypeImp *numberProto = new NumberPrototypeImp(globExec,objProto,funcProto);
+ b_NumberPrototype = Object(numberProto);
+ DatePrototypeImp *dateProto = new DatePrototypeImp(globExec,objProto);
+ b_DatePrototype = Object(dateProto);
+ RegExpPrototypeImp *regexpProto = new RegExpPrototypeImp(globExec,objProto,funcProto);
+ b_RegExpPrototype = Object(regexpProto);
+ ErrorPrototypeImp *errorProto = new ErrorPrototypeImp(globExec,objProto,funcProto);
+ b_ErrorPrototype = Object(errorProto);
+
+ static_cast<ObjectImp*>(global.imp())->setPrototype(b_ObjectPrototype);
+
+ // Constructors (Object, Array, etc.)
+
+ b_Object = Object(new ObjectObjectImp(globExec, objProto, funcProto));
+ b_Function = Object(new FunctionObjectImp(globExec, funcProto));
+ b_Array = Object(new ArrayObjectImp(globExec, funcProto, arrayProto));
+ b_String = Object(new StringObjectImp(globExec, funcProto, stringProto));
+ b_Boolean = Object(new BooleanObjectImp(globExec, funcProto, booleanProto));
+ b_Number = Object(new NumberObjectImp(globExec, funcProto, numberProto));
+ b_Date = Object(new DateObjectImp(globExec, funcProto, dateProto));
+ b_RegExp = Object(new RegExpObjectImp(globExec, funcProto, regexpProto));
+ b_Error = Object(new ErrorObjectImp(globExec, funcProto, errorProto));
+
+ // Error object prototypes
+ b_evalErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,EvalError,
+ "EvalError","EvalError"));
+ b_rangeErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,RangeError,
+ "RangeError","RangeError"));
+ b_referenceErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,ReferenceError,
+ "ReferenceError","ReferenceError"));
+ b_syntaxErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,SyntaxError,
+ "SyntaxError","SyntaxError"));
+ b_typeErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,TypeError,
+ "TypeError","TypeError"));
+ b_uriErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,URIError,
+ "URIError","URIError"));
+
+ // Error objects
+ b_evalError = Object(new NativeErrorImp(globExec,funcProto,b_evalErrorPrototype));
+ b_rangeError = Object(new NativeErrorImp(globExec,funcProto,b_rangeErrorPrototype));
+ b_referenceError = Object(new NativeErrorImp(globExec,funcProto,b_referenceErrorPrototype));
+ b_syntaxError = Object(new NativeErrorImp(globExec,funcProto,b_syntaxErrorPrototype));
+ b_typeError = Object(new NativeErrorImp(globExec,funcProto,b_typeErrorPrototype));
+ b_uriError = Object(new NativeErrorImp(globExec,funcProto,b_uriErrorPrototype));
+
+ // ECMA 15.3.4.1
+ funcProto->put(globExec,constructorPropertyName, b_Function, DontEnum);
+
+ global.put(globExec,"Object", b_Object, DontEnum);
+ global.put(globExec,"Function", b_Function, DontEnum);
+ global.put(globExec,"Array", b_Array, DontEnum);
+ global.put(globExec,"Boolean", b_Boolean, DontEnum);
+ global.put(globExec,"String", b_String, DontEnum);
+ global.put(globExec,"Number", b_Number, DontEnum);
+ global.put(globExec,"Date", b_Date, DontEnum);
+ global.put(globExec,"RegExp", b_RegExp, DontEnum);
+ global.put(globExec,"Error", b_Error, DontEnum);
+ // Using Internal for those to have something != 0
+ // (see kjs_window). Maybe DontEnum would be ok too ?
+ global.put(globExec,"EvalError",b_evalError, Internal);
+ global.put(globExec,"RangeError",b_rangeError, Internal);
+ global.put(globExec,"ReferenceError",b_referenceError, Internal);
+ global.put(globExec,"SyntaxError",b_syntaxError, Internal);
+ global.put(globExec,"TypeError",b_typeError, Internal);
+ global.put(globExec,"URIError",b_uriError, Internal);
+
+ // Set the "constructor" property of all builtin constructors
+ objProto->put(globExec, constructorPropertyName, b_Object, DontEnum | DontDelete | ReadOnly);
+ funcProto->put(globExec, constructorPropertyName, b_Function, DontEnum | DontDelete | ReadOnly);
+ arrayProto->put(globExec, constructorPropertyName, b_Array, DontEnum | DontDelete | ReadOnly);
+ booleanProto->put(globExec, constructorPropertyName, b_Boolean, DontEnum | DontDelete | ReadOnly);
+ stringProto->put(globExec, constructorPropertyName, b_String, DontEnum | DontDelete | ReadOnly);
+ numberProto->put(globExec, constructorPropertyName, b_Number, DontEnum | DontDelete | ReadOnly);
+ dateProto->put(globExec, constructorPropertyName, b_Date, DontEnum | DontDelete | ReadOnly);
+ regexpProto->put(globExec, constructorPropertyName, b_RegExp, DontEnum | DontDelete | ReadOnly);
+ errorProto->put(globExec, constructorPropertyName, b_Error, DontEnum | DontDelete | ReadOnly);
+ b_evalErrorPrototype.put(globExec, constructorPropertyName, b_evalError, DontEnum | DontDelete | ReadOnly);
+ b_rangeErrorPrototype.put(globExec, constructorPropertyName, b_rangeError, DontEnum | DontDelete | ReadOnly);
+ b_referenceErrorPrototype.put(globExec, constructorPropertyName, b_referenceError, DontEnum | DontDelete | ReadOnly);
+ b_syntaxErrorPrototype.put(globExec, constructorPropertyName, b_syntaxError, DontEnum | DontDelete | ReadOnly);
+ b_typeErrorPrototype.put(globExec, constructorPropertyName, b_typeError, DontEnum | DontDelete | ReadOnly);
+ b_uriErrorPrototype.put(globExec, constructorPropertyName, b_uriError, DontEnum | DontDelete | ReadOnly);
+
+ // built-in values
+ global.put(globExec, "NaN", Number(NaN), DontEnum|DontDelete);
+ global.put(globExec, "Infinity", Number(Inf), DontEnum|DontDelete);
+ global.put(globExec, "undefined", Undefined(), DontEnum|DontDelete);
+
+ // built-in functions
+#ifdef KJS_PURE_ECMA // otherwise as deprecated Object.prototype property
+ global.put(globExec,"eval",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::Eval,1,"eval")), DontEnum);
+#endif
+ global.put(globExec,"parseInt",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::ParseInt,2,"parseInt")), DontEnum);
+ global.put(globExec,"parseFloat",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::ParseFloat,1,"parseFloat")), DontEnum);
+ global.put(globExec,"isNaN",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::IsNaN,1,"isNaN")), DontEnum);
+ global.put(globExec,"isFinite",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::IsFinite,1,"isFinite")), DontEnum);
+ global.put(globExec,"decodeURI",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::DecodeURI,1,"decodeURI")),
+ DontEnum);
+ global.put(globExec,"decodeURIComponent",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::DecodeURIComponent,1,"decodeURIComponent")),
+ DontEnum);
+ global.put(globExec,"encodeURI",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::EncodeURI,1,"encodeURI")),
+ DontEnum);
+ global.put(globExec,"encodeURIComponent",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::EncodeURIComponent,1,"encodeURIComponent")),
+ DontEnum);
+ global.put(globExec,"escape",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::Escape,1,"escape")), DontEnum);
+ global.put(globExec,"unescape",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::UnEscape,1,"unescape")), DontEnum);
+#ifndef NDEBUG
+ global.put(globExec,"kjsprint",
+ Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::KJSPrint,1,"kjsprint")), DontEnum);
+#endif
+
+ // built-in objects
+ global.put(globExec,"Math", Object(new MathObjectImp(globExec,objProto)), DontEnum);
+}
+
+InterpreterImp::~InterpreterImp()
+{
+ if (dbg)
+ dbg->detach(m_interpreter);
+ for (SourceCode *s = sources; s; s = s->next)
+ s->interpreter = 0;
+ delete globExec;
+ globExec = 0L;
+ clear();
+}
+
+void InterpreterImp::clear()
+{
+ //fprintf(stderr,"InterpreterImp::clear\n");
+ // remove from global chain (see init())
+ lockInterpreter();
+ next->prev = prev;
+ prev->next = next;
+ s_hook = next;
+ if (s_hook == this)
+ {
+ // This was the last interpreter
+ s_hook = 0L;
+ globalClear();
+ }
+ unlockInterpreter();
+}
+
+void InterpreterImp::mark()
+{
+ //if (exVal && !exVal->marked())
+ // exVal->mark();
+ //if (retVal && !retVal->marked())
+ // retVal->mark();
+ if (UndefinedImp::staticUndefined && !UndefinedImp::staticUndefined->marked())
+ UndefinedImp::staticUndefined->mark();
+ if (NullImp::staticNull && !NullImp::staticNull->marked())
+ NullImp::staticNull->mark();
+ if (NumberImp::staticNaN && !NumberImp::staticNaN->marked())
+ NumberImp::staticNaN->mark();
+ if (BooleanImp::staticTrue && !BooleanImp::staticTrue->marked())
+ BooleanImp::staticTrue->mark();
+ if (BooleanImp::staticFalse && !BooleanImp::staticFalse->marked())
+ BooleanImp::staticFalse->mark();
+ //fprintf( stderr, "InterpreterImp::mark this=%p global.imp()=%p\n", this, global.imp() );
+ if (global.imp())
+ global.imp()->mark();
+ if (m_interpreter)
+ m_interpreter->mark();
+ if (_context)
+ _context->mark();
+}
+
+bool InterpreterImp::checkSyntax(const UString &code, int *errLine, UString *errMsg)
+{
+ // Parser::parse() returns 0 in a syntax error occurs, so we just check for that
+ SourceCode *source;
+ FunctionBodyNode *progNode = Parser::parse(code.data(),code.size(),&source,errLine,errMsg);
+ source->deref();
+ bool ok = (progNode != 0);
+ delete progNode;
+ return ok;
+}
+
+bool InterpreterImp::checkSyntax(const UString &code)
+{
+ // Parser::parse() returns 0 in a syntax error occurs, so we just check for that
+ SourceCode *source;
+ FunctionBodyNode *progNode = Parser::parse(code.data(),code.size(),&source,0,0);
+ source->deref();
+ bool ok = (progNode != 0);
+ delete progNode;
+ return ok;
+}
+
+Completion InterpreterImp::evaluate(const UString &code, const Value &thisV)
+{
+ lockInterpreter();
+
+ // prevent against infinite recursion
+ if (recursion >= 20) {
+ Completion result = Completion(Throw,Error::create(globExec,GeneralError,"Recursion too deep"));
+ unlockInterpreter();
+ return result;
+ }
+
+ // parse the source code
+ int errLine;
+ UString errMsg;
+ SourceCode *source;
+ FunctionBodyNode *progNode = Parser::parse(code.data(),code.size(),&source,&errLine,&errMsg);
+
+ // notify debugger that source has been parsed
+ if (dbg) {
+ bool cont = dbg->sourceParsed(globExec,source->sid,code,errLine);
+ if (!cont) {
+ source->deref();
+ if (progNode)
+ delete progNode;
+ unlockInterpreter();
+ return Completion(Break);
+ }
+ }
+
+ addSourceCode(source);
+
+ // no program node means a syntax error occurred
+ if (!progNode) {
+ Object err = Error::create(globExec,SyntaxError,errMsg.ascii(),errLine);
+ err.put(globExec,"sid",Number(source->sid));
+ globExec->setException(err); // required to notify the debugger
+ globExec->clearException();
+ source->deref();
+ unlockInterpreter();
+ return Completion(Throw,err);
+ }
+ source->deref();
+
+ globExec->clearException();
+
+ recursion++;
+ progNode->ref();
+
+ Object &globalObj = globalObject();
+ Object thisObj = globalObject();
+
+ if (thisV.isValid()) {
+ // "this" must be an object... use same rules as Function.prototype.apply()
+ if (thisV.isA(NullType) || thisV.isA(UndefinedType))
+ thisObj = globalObject();
+ else {
+ thisObj = thisV.toObject(globExec);
+ }
+ }
+
+ Completion res;
+ if (globExec->hadException()) {
+ // the thisArg.toObject() conversion above might have thrown an exception - if so,
+ // propagate it back
+ res = Completion(Throw,globExec->exception());
+ }
+ else {
+ // execute the code
+ ContextImp ctx(globalObj, this, thisObj, source->sid);
+ ExecState newExec(m_interpreter,&ctx);
+
+ // create variables (initialized to undefined until var statements
+ // with optional initializers are executed)
+ progNode->processVarDecls(&newExec);
+
+ ctx.setLines(progNode->firstLine(),progNode->firstLine());
+ bool abort = false;
+ if (dbg) {
+ if (!dbg->enterContext(&newExec)) {
+ // debugger requested we stop execution
+ dbg->imp()->abort();
+ abort = true;
+ }
+ }
+
+ if (!abort) {
+ ctx.setLines(progNode->lastLine(),progNode->lastLine());
+ res = progNode->execute(&newExec);
+ if (dbg && !dbg->exitContext(&newExec,res)) {
+ // debugger requested we stop execution
+ dbg->imp()->abort();
+ unlockInterpreter();
+ res = Completion(ReturnValue,Undefined());
+ }
+ }
+ }
+
+ if (progNode->deref())
+ delete progNode;
+ recursion--;
+
+ if (globExec->hadException()) {
+ res = Completion(Throw,globExec->exception());
+ globExec->clearException();
+ }
+
+ unlockInterpreter();
+ return res;
+}
+
+void InterpreterImp::setDebugger(Debugger *d)
+{
+ if (d == dbg)
+ return;
+ // avoid recursion
+ Debugger *old = dbg;
+ dbg = d;
+ if ( old )
+ old->detach(m_interpreter);
+}
+
+void InterpreterImp::addSourceCode(SourceCode *code)
+{
+ assert(!code->next);
+ assert(!code->interpreter);
+ code->next = sources;
+ code->interpreter = this;
+ sources = code;
+}
+
+void InterpreterImp::removeSourceCode(SourceCode *code)
+{
+ assert(code);
+ assert(sources);
+
+ if (code == sources) {
+ sources = sources->next;
+ return;
+ }
+
+ SourceCode *prev = sources;
+ SourceCode *cur = sources->next;
+ while (cur != code) {
+ assert(cur);
+ prev = cur;
+ cur = cur->next;
+ }
+
+ prev->next = cur->next;
+}
+
+// ------------------------------ InternalFunctionImp --------------------------
+
+const ClassInfo InternalFunctionImp::info = {"Function", 0, 0, 0};
+
+InternalFunctionImp::InternalFunctionImp(FunctionPrototypeImp *funcProto)
+ : ObjectImp(funcProto)
+{
+}
+
+InternalFunctionImp::InternalFunctionImp(ExecState *exec)
+ : ObjectImp(static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp()))
+{
+}
+
+bool InternalFunctionImp::implementsHasInstance() const
+{
+ return true;
+}
+
+Boolean InternalFunctionImp::hasInstance(ExecState *exec, const Value &value)
+{
+ if (value.type() != ObjectType)
+ return Boolean(false);
+
+ Value prot = get(exec,prototypePropertyName);
+ if (prot.type() != ObjectType && prot.type() != NullType) {
+ Object err = Error::create(exec, TypeError, "Invalid prototype encountered "
+ "in instanceof operation.");
+ exec->setException(err);
+ return Boolean(false);
+ }
+
+ Object v = Object(static_cast<ObjectImp*>(value.imp()));
+ while ((v = Object::dynamicCast(v.prototype())).imp()) {
+ if (v.imp() == prot.imp())
+ return Boolean(true);
+ }
+ return Boolean(false);
+}
+
+// ------------------------------ global functions -----------------------------
+
+double KJS::roundValue(ExecState *exec, const Value &v)
+{
+ double n = v.toNumber(exec);
+ if (isNaN(n) || isInf(n))
+ return n;
+ double an = fabs(n);
+ if (an == 0.0)
+ return n;
+ double d = floor(an);
+ if (n < 0)
+ d *= -1;
+
+ return d;
+}
+
+#ifndef NDEBUG
+#include <stdio.h>
+void KJS::printInfo(ExecState *exec, const char *s, const Value &o, int lineno)
+{
+ if (!o.isValid())
+ fprintf(stderr, "KJS: %s: (null)", s);
+ else {
+ Value v = o;
+ unsigned int arrayLength = 0;
+ bool hadExcep = exec->hadException();
+
+ 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: {
+ Object obj = Object::dynamicCast(v);
+ name = obj.className();
+ if (name.isNull())
+ name = "(unknown class)";
+ if ( obj.inherits(&ArrayInstanceImp::info) )
+ arrayLength = obj.get(exec,lengthPropertyName).toUInt32(exec);
+ }
+ break;
+ }
+ UString vString;
+ // Avoid calling toString on a huge array (e.g. 4 billion elements, in mozilla/js/js1_5/Array/array-001.js)
+ if ( arrayLength > 100 )
+ vString = UString( "[ Array with " ) + UString::from( arrayLength ) + " elements ]";
+ else
+ vString = v.toString(exec);
+ if ( !hadExcep )
+ exec->clearException();
+ 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.imp());
+
+ if (lineno >= 0)
+ fprintf(stderr, ", line %d\n",lineno);
+ else
+ fprintf(stderr, "\n");
+ }
+}
+#endif
diff --git a/kjs/internal.h b/kjs/internal.h
new file mode 100644
index 000000000..413fdaa52
--- /dev/null
+++ b/kjs/internal.h
@@ -0,0 +1,508 @@
+// -*- 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 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 _INTERNAL_H_
+#define _INTERNAL_H_
+
+#include "ustring.h"
+#include "value.h"
+#include "object.h"
+#include "function.h"
+#include "types.h"
+#include "interpreter.h"
+#include "scope_chain.h"
+#include "array_instance.h"
+
+#ifndef I18N_NOOP
+#define I18N_NOOP(s) s
+#endif
+
+namespace KJS {
+
+ static const double D16 = 65536.0;
+ static const double D32 = 4294967296.0;
+
+ class FunctionBodyNode;
+ class FunctionBodyNode;
+ class FunctionPrototypeImp;
+ class FunctionImp;
+ class Parameter;
+ class Debugger;
+
+ // ---------------------------------------------------------------------------
+ // Primitive impls
+ // ---------------------------------------------------------------------------
+
+ class UndefinedImp : public ValueImp {
+ public:
+ Type type() const { return UndefinedType; }
+
+ Value toPrimitive(ExecState *exec, Type preferred = UnspecifiedType) const;
+ bool toBoolean(ExecState *exec) const;
+ double toNumber(ExecState *exec) const;
+ UString toString(ExecState *exec) const;
+ Object toObject(ExecState *exec) const;
+
+ static UndefinedImp *staticUndefined;
+ };
+
+ inline Undefined::Undefined(UndefinedImp *imp) : Value(imp) { }
+
+ class NullImp : public ValueImp {
+ public:
+ Type type() const { return NullType; }
+
+ Value toPrimitive(ExecState *exec, Type preferred = UnspecifiedType) const;
+ bool toBoolean(ExecState *exec) const;
+ double toNumber(ExecState *exec) const;
+ UString toString(ExecState *exec) const;
+ Object toObject(ExecState *exec) const;
+
+ static NullImp *staticNull;
+ };
+
+ inline Null::Null(NullImp *imp) : Value(imp) { }
+
+ class BooleanImp : public ValueImp {
+ public:
+ BooleanImp(bool v = false) : val(v) { }
+ bool value() const { return val; }
+
+ Type type() const { return BooleanType; }
+
+ Value toPrimitive(ExecState *exec, Type preferred = UnspecifiedType) const;
+ bool toBoolean(ExecState *exec) const;
+ double toNumber(ExecState *exec) const;
+ UString toString(ExecState *exec) const;
+ Object toObject(ExecState *exec) const;
+
+ static BooleanImp *staticTrue;
+ static BooleanImp *staticFalse;
+ private:
+ bool val;
+ };
+
+ inline Boolean::Boolean(BooleanImp *imp) : Value(imp) { }
+
+ class StringImp : public ValueImp {
+ public:
+ StringImp(const UString& v) : val(v) { }
+ UString value() const { return val; }
+
+ Type type() const { return StringType; }
+
+ Value toPrimitive(ExecState *exec, Type preferred = UnspecifiedType) const;
+ bool toBoolean(ExecState *exec) const;
+ double toNumber(ExecState *exec) const;
+ UString toString(ExecState *exec) const;
+ Object toObject(ExecState *exec) const;
+
+ private:
+ UString val;
+ };
+
+ inline String::String(StringImp *imp) : Value(imp) { }
+
+ class NumberImp : public ValueImp {
+ friend class Number;
+ friend class InterpreterImp;
+ public:
+ static ValueImp *create(int);
+ static ValueImp *create(double);
+ static ValueImp *zero() { return SimpleNumber::make(0); }
+ static ValueImp *one() { return SimpleNumber::make(1); }
+ static ValueImp *two() { return SimpleNumber::make(2); }
+
+ double value() const { return val; }
+
+ Type type() const { return NumberType; }
+
+ Value toPrimitive(ExecState *exec, Type preferred = UnspecifiedType) const;
+ bool toBoolean(ExecState *exec) const;
+ double toNumber(ExecState *exec) const;
+ UString toString(ExecState *exec) const;
+ Object toObject(ExecState *exec) const;
+
+ static NumberImp *staticNaN;
+
+ private:
+ NumberImp(double v) : val(v) { }
+
+ virtual bool toUInt32(unsigned&) const;
+
+ double val;
+ };
+
+ inline Number::Number(NumberImp *imp) : Value(imp) { }
+
+ /**
+ * @short The "label set" in Ecma-262 spec
+ */
+ class LabelStack {
+ public:
+ LabelStack(): tos(0L), iterationDepth(0), switchDepth(0) {}
+ ~LabelStack();
+
+ LabelStack(const LabelStack &other);
+ LabelStack &operator=(const LabelStack &other);
+
+ /**
+ * 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();
+
+ void pushIteration() { iterationDepth++; }
+ void popIteration() { iterationDepth--; }
+ bool inIteration() const { return (iterationDepth > 0); }
+
+ void pushSwitch() { switchDepth++; }
+ void popSwitch() { switchDepth--; }
+ bool inSwitch() const { return (switchDepth > 0); }
+
+ private:
+ struct StackElem {
+ Identifier id;
+ StackElem *prev;
+ };
+
+ StackElem *tos;
+ void clear();
+ int iterationDepth;
+ int switchDepth;
+ };
+
+
+ // ---------------------------------------------------------------------------
+ // Parsing & evaluateion
+ // ---------------------------------------------------------------------------
+
+ class SourceCode {
+ public:
+ SourceCode(int _sid)
+ : sid(_sid), interpreter(0), refcount(0), next(0) {}
+
+ void ref() { refcount++; }
+ void deref() { if (!--refcount) cleanup(); }
+ void cleanup();
+
+ int sid;
+ InterpreterImp *interpreter;
+ int refcount;
+ SourceCode *next;
+ };
+
+ /**
+ * @internal
+ *
+ * Parses ECMAScript source code and converts into FunctionBodyNode objects, which
+ * represent the root of a parse tree. This class provides a conveniant workaround
+ * for the problem of the bison parser working in a static context.
+ */
+ class Parser {
+ public:
+ static FunctionBodyNode *parse(const UChar *code, unsigned int length, SourceCode **src,
+ int *errLine = 0, UString *errMsg = 0);
+
+ static FunctionBodyNode *progNode;
+ static SourceCode *source;
+ static int sid;
+ private:
+ };
+
+ class InterpreterImp {
+ friend class Collector;
+ public:
+ static void globalInit();
+ static void globalClear();
+
+ InterpreterImp(Interpreter *interp, const Object &glob);
+ ~InterpreterImp();
+
+ Object &globalObject() const { return const_cast<Object &>(global); }
+ Interpreter* interpreter() const { return m_interpreter; }
+
+ void initGlobalObject();
+ static void lock();
+ static void unlock();
+
+ void mark();
+
+ ExecState *globalExec() { return globExec; }
+ bool checkSyntax(const UString &code,int *errLine, UString *errMsg);
+ bool checkSyntax(const UString &code);
+ Completion evaluate(const UString &code, const Value &thisV);
+ Debugger *debugger() const { return dbg; }
+ void setDebugger(Debugger *d);
+
+ Object builtinObject() const { return b_Object; }
+ Object builtinFunction() const { return b_Function; }
+ Object builtinArray() const { return b_Array; }
+ Object builtinBoolean() const { return b_Boolean; }
+ Object builtinString() const { return b_String; }
+ Object builtinNumber() const { return b_Number; }
+ Object builtinDate() const { return b_Date; }
+ Object builtinRegExp() const { return b_RegExp; }
+ Object builtinError() const { return b_Error; }
+
+ Object builtinObjectPrototype() const { return b_ObjectPrototype; }
+ Object builtinFunctionPrototype() const { return b_FunctionPrototype; }
+ Object builtinArrayPrototype() const { return b_ArrayPrototype; }
+ Object builtinBooleanPrototype() const { return b_BooleanPrototype; }
+ Object builtinStringPrototype() const { return b_StringPrototype; }
+ Object builtinNumberPrototype() const { return b_NumberPrototype; }
+ Object builtinDatePrototype() const { return b_DatePrototype; }
+ Object builtinRegExpPrototype() const { return b_RegExpPrototype; }
+ Object builtinErrorPrototype() const { return b_ErrorPrototype; }
+
+ Object builtinEvalError() const { return b_evalError; }
+ Object builtinRangeError() const { return b_rangeError; }
+ Object builtinReferenceError() const { return b_referenceError; }
+ Object builtinSyntaxError() const { return b_syntaxError; }
+ Object builtinTypeError() const { return b_typeError; }
+ Object builtinURIError() const { return b_uriError; }
+
+ Object builtinEvalErrorPrototype() const { return b_evalErrorPrototype; }
+ Object builtinRangeErrorPrototype() const { return b_rangeErrorPrototype; }
+ Object builtinReferenceErrorPrototype() const { return b_referenceErrorPrototype; }
+ Object builtinSyntaxErrorPrototype() const { return b_syntaxErrorPrototype; }
+ Object builtinTypeErrorPrototype() const { return b_typeErrorPrototype; }
+ Object builtinURIErrorPrototype() const { return b_uriErrorPrototype; }
+
+ void setCompatMode(Interpreter::CompatMode mode) { m_compatMode = mode; }
+ Interpreter::CompatMode compatMode() const { return m_compatMode; }
+
+ // Chained list of interpreters (ring)
+ static InterpreterImp* firstInterpreter() { return s_hook; }
+ InterpreterImp *nextInterpreter() const { return next; }
+ InterpreterImp *prevInterpreter() const { return prev; }
+
+ void addSourceCode(SourceCode *code);
+ void removeSourceCode(SourceCode *code);
+
+ void setContext(ContextImp *c) { _context = c; }
+
+ private:
+ void clear();
+ Interpreter *m_interpreter;
+ Object global;
+ Debugger *dbg;
+
+ // Built-in properties of the object prototype. These are accessible
+ // from here even if they are replaced by js code (e.g. assigning to
+ // Array.prototype)
+
+ Object b_Object;
+ Object b_Function;
+ Object b_Array;
+ Object b_Boolean;
+ Object b_String;
+ Object b_Number;
+ Object b_Date;
+ Object b_RegExp;
+ Object b_Error;
+
+ Object b_ObjectPrototype;
+ Object b_FunctionPrototype;
+ Object b_ArrayPrototype;
+ Object b_BooleanPrototype;
+ Object b_StringPrototype;
+ Object b_NumberPrototype;
+ Object b_DatePrototype;
+ Object b_RegExpPrototype;
+ Object b_ErrorPrototype;
+
+ Object b_evalError;
+ Object b_rangeError;
+ Object b_referenceError;
+ Object b_syntaxError;
+ Object b_typeError;
+ Object b_uriError;
+
+ Object b_evalErrorPrototype;
+ Object b_rangeErrorPrototype;
+ Object b_referenceErrorPrototype;
+ Object b_syntaxErrorPrototype;
+ Object b_typeErrorPrototype;
+ Object b_uriErrorPrototype;
+
+ ExecState *globExec;
+ Interpreter::CompatMode m_compatMode;
+
+ // Chained list of interpreters (ring) - for collector
+ static InterpreterImp* s_hook;
+ InterpreterImp *next, *prev;
+
+ ContextImp *_context;
+
+ int recursion;
+ SourceCode *sources;
+ };
+
+ class AttachedInterpreter;
+ class DebuggerImp {
+ public:
+
+ DebuggerImp() {
+ interps = 0;
+ isAborted = false;
+ }
+
+ void abort() { isAborted = true; }
+ bool aborted() const { return isAborted; }
+
+ AttachedInterpreter *interps;
+ bool isAborted;
+ };
+
+ /**
+ * @short Implementation class for functions implemented in JS.
+ */
+ class FunctionImp : public InternalFunctionImp {
+ friend class ActivationImp;
+ public:
+ FunctionImp(ExecState *exec, const Identifier &n = Identifier::null());
+ virtual ~FunctionImp();
+
+ virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+ virtual void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr = None);
+ virtual bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
+ virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ void addParameter(const Identifier &n);
+ Identifier parameterProperty(int index) const;
+ // parameters in string representation, e.g. (a, b, c)
+ UString parameterString() const;
+ virtual CodeType codeType() const = 0;
+
+ virtual Completion execute(ExecState *exec) = 0;
+ int firstLine() const { return line0; }
+ int lastLine() const { return line1; }
+ int sourceId() const { return sid; }
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ protected:
+ Parameter *param;
+ int line0;
+ int line1;
+ int sid;
+
+ private:
+ void processParameters(ExecState *exec, const List &);
+ virtual void processVarDecls(ExecState *exec);
+ };
+
+ class DeclaredFunctionImp : public FunctionImp {
+ public:
+ DeclaredFunctionImp(ExecState *exec, const Identifier &n,
+ FunctionBodyNode *b, const ScopeChain &sc);
+ ~DeclaredFunctionImp();
+
+ bool implementsConstruct() const;
+ Object construct(ExecState *exec, const List &args);
+
+ virtual Completion execute(ExecState *exec);
+ CodeType codeType() const { return FunctionCode; }
+ FunctionBodyNode *body;
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ KJS_EXPORT static const ClassInfo info;
+ private:
+ virtual void processVarDecls(ExecState *exec);
+ };
+
+ class ActivationImp;
+
+ class ArgumentsImp : public ObjectImp {
+ public:
+ ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act);
+
+ virtual void mark();
+
+ virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+ virtual void put(ExecState *exec, const Identifier &propertyName,
+ const Value &value, int attr = None);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+
+ private:
+ ActivationImp *activation;
+ };
+
+ class ActivationImp : public ObjectImp {
+ public:
+ ActivationImp(FunctionImp *function, const List &arguments);
+
+ virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+ virtual bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
+ virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+
+ virtual void mark();
+
+ private:
+ FunctionImp *_function;
+ List _arguments;
+ mutable ArgumentsImp *_argumentsObject;
+ };
+
+ class GlobalFuncImp : public InternalFunctionImp {
+ public:
+ GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ virtual CodeType codeType() const;
+ enum { Eval, ParseInt, ParseFloat, IsNaN, IsFinite, DecodeURI, DecodeURIComponent,
+ EncodeURI, EncodeURIComponent, Escape, UnEscape, KJSPrint };
+ private:
+ int id;
+ };
+
+ // helper function for toInteger, toInt32, toUInt32 and toUInt16
+ double roundValue(ExecState *exec, const Value &v);
+
+#ifndef NDEBUG
+ void printInfo(ExecState *exec, const char *s, const Value &o, int lineno = -1);
+#endif
+
+} // namespace
+
+
+#endif // _INTERNAL_H_
diff --git a/kjs/interpreter.cpp b/kjs/interpreter.cpp
new file mode 100644
index 000000000..8d4a7eca7
--- /dev/null
+++ b/kjs/interpreter.cpp
@@ -0,0 +1,413 @@
+// -*- 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "internal.h"
+#include "collector.h"
+#include "operations.h"
+#include "error_object.h"
+#include "debugger.h"
+#include "nodes.h"
+#include "context.h"
+
+using namespace KJS;
+
+// ------------------------------ Context --------------------------------------
+
+const ScopeChain &Context::scopeChain() const
+{
+ return rep->scopeChain();
+}
+
+Object Context::variableObject() const
+{
+ return rep->variableObject();
+}
+
+Object Context::thisValue() const
+{
+ return rep->thisValue();
+}
+
+const Context Context::callingContext() const
+{
+ return rep->callingContext();
+}
+
+CodeType Context::codeType() const
+{
+ return rep->codeType();
+}
+
+int Context::sourceId() const
+{
+ return rep->sourceId;
+}
+
+int Context::curStmtFirstLine() const
+{
+ return rep->line0;
+}
+
+int Context::curStmtLastLine() const
+{
+ return rep->line1;
+}
+
+Object Context::function() const
+{
+ return Object(rep->function());
+}
+
+Identifier Context::functionName() const
+{
+ return rep->functionName;
+}
+
+List Context::args() const
+{
+ return rep->args;
+}
+
+bool KJS::operator==(const Context &c1, const Context &c2)
+{
+ return (c1.imp() == c2.imp());
+}
+
+bool KJS::operator!=(const Context &c1, const Context &c2)
+{
+ return (c1.imp() != c2.imp());
+}
+
+// ------------------------------ Interpreter ---------------------------------
+
+Interpreter::Interpreter(const Object &global)
+{
+ rep = new InterpreterImp(this,global);
+}
+
+Interpreter::Interpreter()
+{
+ Object global(new ObjectImp());
+ rep = new InterpreterImp(this,global);
+}
+
+Interpreter::~Interpreter()
+{
+ delete rep;
+}
+
+Object &Interpreter::globalObject() const
+{
+ return rep->globalObject();
+}
+
+void Interpreter::initGlobalObject()
+{
+ rep->initGlobalObject();
+}
+
+void Interpreter::lock()
+{
+ InterpreterImp::lock();
+}
+
+void Interpreter::unlock()
+{
+ InterpreterImp::unlock();
+}
+
+ExecState *Interpreter::globalExec()
+{
+ return rep->globalExec();
+}
+
+bool Interpreter::checkSyntax(const UString &code, int *errLine, UString *errMsg)
+{
+ return rep->checkSyntax(code,errLine,errMsg);
+}
+
+bool Interpreter::checkSyntax(const UString &code)
+{
+ return rep->checkSyntax(code);
+}
+
+Completion Interpreter::evaluate(const UString &code, const Value &thisV)
+{
+ return rep->evaluate(code,thisV);
+}
+
+InterpreterImp *Interpreter::imp()
+{
+ return rep;
+}
+
+Object Interpreter::builtinObject() const
+{
+ return rep->builtinObject();
+}
+
+Object Interpreter::builtinFunction() const
+{
+ return rep->builtinFunction();
+}
+
+Object Interpreter::builtinArray() const
+{
+ return rep->builtinArray();
+}
+
+Object Interpreter::builtinBoolean() const
+{
+ return rep->builtinBoolean();
+}
+
+Object Interpreter::builtinString() const
+{
+ return rep->builtinString();
+}
+
+Object Interpreter::builtinNumber() const
+{
+ return rep->builtinNumber();
+}
+
+Object Interpreter::builtinDate() const
+{
+ return rep->builtinDate();
+}
+
+Object Interpreter::builtinRegExp() const
+{
+ return rep->builtinRegExp();
+}
+
+Object Interpreter::builtinError() const
+{
+ return rep->builtinError();
+}
+
+Object Interpreter::builtinObjectPrototype() const
+{
+ return rep->builtinObjectPrototype();
+}
+
+Object Interpreter::builtinFunctionPrototype() const
+{
+ return rep->builtinFunctionPrototype();
+}
+
+Object Interpreter::builtinArrayPrototype() const
+{
+ return rep->builtinArrayPrototype();
+}
+
+Object Interpreter::builtinBooleanPrototype() const
+{
+ return rep->builtinBooleanPrototype();
+}
+
+Object Interpreter::builtinStringPrototype() const
+{
+ return rep->builtinStringPrototype();
+}
+
+Object Interpreter::builtinNumberPrototype() const
+{
+ return rep->builtinNumberPrototype();
+}
+
+Object Interpreter::builtinDatePrototype() const
+{
+ return rep->builtinDatePrototype();
+}
+
+Object Interpreter::builtinRegExpPrototype() const
+{
+ return rep->builtinRegExpPrototype();
+}
+
+Object Interpreter::builtinErrorPrototype() const
+{
+ return rep->builtinErrorPrototype();
+}
+
+Object Interpreter::builtinEvalError() const
+{
+ return rep->builtinEvalError();
+}
+
+Object Interpreter::builtinRangeError() const
+{
+ return rep->builtinRangeError();
+}
+
+Object Interpreter::builtinReferenceError() const
+{
+ return rep->builtinReferenceError();
+}
+
+Object Interpreter::builtinSyntaxError() const
+{
+ return rep->builtinSyntaxError();
+}
+
+Object Interpreter::builtinTypeError() const
+{
+ return rep->builtinTypeError();
+}
+
+Object Interpreter::builtinURIError() const
+{
+ return rep->builtinURIError();
+}
+
+Object Interpreter::builtinEvalErrorPrototype() const
+{
+ return rep->builtinEvalErrorPrototype();
+}
+
+Object Interpreter::builtinRangeErrorPrototype() const
+{
+ return rep->builtinRangeErrorPrototype();
+}
+
+Object Interpreter::builtinReferenceErrorPrototype() const
+{
+ return rep->builtinReferenceErrorPrototype();
+}
+
+Object Interpreter::builtinSyntaxErrorPrototype() const
+{
+ return rep->builtinSyntaxErrorPrototype();
+}
+
+Object Interpreter::builtinTypeErrorPrototype() const
+{
+ return rep->builtinTypeErrorPrototype();
+}
+
+Object Interpreter::builtinURIErrorPrototype() const
+{
+ return rep->builtinURIErrorPrototype();
+}
+
+void Interpreter::setCompatMode(CompatMode mode)
+{
+ rep->setCompatMode(mode);
+}
+
+Interpreter::CompatMode Interpreter::compatMode() const
+{
+ return rep->compatMode();
+}
+
+bool Interpreter::collect()
+{
+ return Collector::collect();
+}
+
+#ifdef KJS_DEBUG_MEM
+#include "lexer.h"
+void Interpreter::finalCheck()
+{
+ fprintf(stderr,"Interpreter::finalCheck()\n");
+ // Garbage collect - as many times as necessary
+ // (we could delete an object which was holding another object, so
+ // the deref() will happen too late for deleting the impl of the 2nd object).
+ while( Collector::collect() )
+ ;
+
+ Node::finalCheck();
+ Collector::finalCheck();
+ Lexer::globalClear();
+ UString::globalClear();
+}
+#endif
+
+// ------------------------------ ExecState --------------------------------------
+
+void ExecState::setException(const Value &e)
+{
+ if (e.isValid()) {
+ Debugger *dbg = _interpreter->imp()->debugger();
+ if (dbg)
+ dbg->exception(this,e,_context->inTryCatch());
+ }
+ _exception = e;
+}
+
+void ExecState::clearException()
+{
+ terminate_request = false;
+ _exception = Value();
+}
+
+bool ExecState::terminate_request = false;
+
+static bool defaultConfirm() { return true; }
+
+bool (*ExecState::confirmTerminate)() = defaultConfirm;
+
+bool ExecState::hadException()
+{
+ if (terminate_request) {
+ terminate_request = false;
+ if (confirmTerminate())
+ _exception = Error::create((ExecState*)this);
+ }
+ return _exception.isValid();
+}
+
+void Interpreter::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+
+Interpreter *ExecState::lexicalInterpreter() const
+{
+ // TODO: use proper implementation
+#if 1
+ return dynamicInterpreter();
+#else
+ if (!_context) {
+ return dynamicInterpreter();
+ }
+
+ InterpreterImp *result = InterpreterImp::interpreterWithGlobalObject(_context->scopeChain().bottom());
+
+ if (!result) {
+ return dynamicInterpreter();
+ }
+
+ return result->interpreter();
+#endif
+}
diff --git a/kjs/interpreter.h b/kjs/interpreter.h
new file mode 100644
index 000000000..e8f4f84f3
--- /dev/null
+++ b/kjs/interpreter.h
@@ -0,0 +1,498 @@
+// -*- 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 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_INTERPRETER_H_
+#define _KJS_INTERPRETER_H_
+
+#include "value.h"
+#include "object.h"
+#include "types.h"
+
+namespace KJS {
+
+ class ContextImp;
+ class InterpreterImp;
+
+ /**
+ * The three different types of code that can be executed in a Context.
+ * These are:
+ * <ul>
+ * <li>GlobalCode - code executed as a result of a call to
+ * Interpreter::evaluate().</li>
+ * <li>EvalCode - executed by a call to the builtin eval() function</li>
+ * <li>FunctionCode - inside a function call (ECMAScript functions only;
+ * does not include builtin native functions or funcitons supplied by the
+ * host environment</li>
+ * </ul>
+ */
+ enum CodeType {
+ GlobalCode = 0,
+ EvalCode = 1,
+ FunctionCode = 2
+ };
+
+ /**
+ * Represents an execution context, as specified by section 10 of the ECMA
+ * spec.
+ *
+ * An execution context contains information about the current state of the
+ * script - the scope for variable lookup, the value of "this", etc. A new
+ * execution context is entered whenever global code is executed (e.g. with
+ * Interpreter::evaluate()), a function is called (see
+ * Object::call()), or the builtin "eval" function is executed.
+ *
+ * Most inheritable functions in the KJS api take a ExecState pointer as
+ * their first parameter. This can be used to obtain a handle to the current
+ * execution context.
+ *
+ * Note: Context objects are wrapper classes/smart pointers for the internal
+ * KJS ContextImp type. When one context variable is assigned to another, it
+ * is still referencing the same internal object.
+ */
+ class KJS_EXPORT Context {
+ public:
+ Context(ContextImp *i) : rep(i) { }
+
+ ContextImp *imp() const { return rep; }
+
+ /**
+ * Returns the scope chain for this execution context. This is used for
+ * variable lookup, with the list being searched from start to end until a
+ * variable is found.
+ *
+ * @return The execution context's scope chain
+ */
+ const ScopeChain &scopeChain() const;
+
+ /**
+ * Returns the variable object for the execution context. This contains a
+ * property for each variable declared in the execution context.
+ *
+ * @return The execution context's variable object
+ */
+ Object variableObject() const;
+
+ /**
+ * Returns the "this" value for the execution context. This is the value
+ * returned when a script references the special variable "this". It should
+ * always be an Object, unless application-specific code has passed in a
+ * different type.
+ *
+ * The object that is used as the "this" value depends on the type of
+ * execution context - for global contexts, the global object is used. For
+ * function objewcts, the value is given by the caller (e.g. in the case of
+ * obj.func(), obj would be the "this" value). For code executed by the
+ * built-in "eval" function, the this value is the same as the calling
+ * context.
+ *
+ * @return The execution context's "this" value
+ */
+ Object thisValue() const;
+
+ /**
+ * Returns the context from which the current context was invoked. For
+ * global code this will be a null context (i.e. one for which
+ * isNull() returns true). You should check isNull() on the returned
+ * value before calling any of it's methods.
+ *
+ * @return The calling execution context
+ */
+ const Context callingContext() const;
+
+ /**
+ * The type of code being executed in this context. One of GlobalCode,
+ * EvalCode or FunctionCode
+ */
+ CodeType codeType() const;
+
+ /**
+ * The identifier of the source code fragment containing the code being
+ * executed
+ */
+ int sourceId() const;
+
+ /**
+ * The line number on which the current statement begins
+ */
+ int curStmtFirstLine() const;
+
+ /**
+ * The line number on which the current statement ends
+ */
+ int curStmtLastLine() const;
+
+ /**
+ * In the case of FunctionCode, the function objects being called
+ */
+ Object function() const;
+
+ /**
+ * In the case of FunctionCode, the name of the function being called
+ */
+ Identifier functionName() const;
+
+ /**
+ * In the case of FunctionCode, the arguments passed to the function
+ */
+ List args() const;
+
+ private:
+ ContextImp *rep;
+ };
+
+ bool operator==(const Context &c1, const Context &c2);
+ bool operator!=(const Context &c1, const Context &c2);
+
+ /**
+ * Interpreter objects can be used to evaluate ECMAScript code. Each
+ * interpreter has a global object which is used for the purposes of code
+ * evaluation, and also provides access to built-in properties such as
+ * " Object" and "Number".
+ */
+ class KJS_EXPORT Interpreter {
+ public:
+ /**
+ * Creates a new interpreter. The supplied object will be used as the global
+ * object for all scripts executed with this interpreter. During
+ * constuction, all the standard properties such as "Object" and "Number"
+ * will be added to the global object.
+ *
+ * Note: You should not use the same global object for multiple
+ * interpreters.
+ *
+ * This is due do the fact that the built-in properties are set in the
+ * constructor, and if these objects have been modified from another
+ * interpreter (e.g. a script modifying String.prototype), the changes will
+ * be overridden.
+ *
+ * @param global The object to use as the global object for this interpreter
+ */
+ Interpreter(const Object &global);
+ /**
+ * Creates a new interpreter. A global object will be created and
+ * initialized with the standard global properties.
+ */
+ Interpreter();
+ virtual ~Interpreter();
+
+ /**
+ * Returns the object that is used as the global object during all script
+ * execution performed by this interpreter
+ */
+ Object &globalObject() const;
+
+ void initGlobalObject();
+
+ static void lock();
+ static void unlock();
+
+ /**
+ * Returns the execution state object which can be used to execute
+ * scripts using this interpreter at a the "global" level, i.e. one
+ * with a execution context that has the global object as the "this"
+ * value, and who's scope chain contains only the global object.
+ *
+ * Note: this pointer remains constant for the life of the interpreter
+ * and should not be manually deleted.
+ *
+ * @return The interpreter global execution state object
+ */
+ ExecState *globalExec();
+
+ /**
+ * Parses the supplied ECMAScript code and checks for syntax errors.
+ *
+ * @param code The code to check
+ * @param errLine Returns the line the error was on (if there was one).
+ * @param errMsg Returns the error message (if there was one).
+ * @return true if there were no syntax errors in the code, otherwise false
+ */
+ bool checkSyntax(const UString &code, int *errLine, UString *errMsg);
+
+ /**
+ * Parses the supplied ECMAScript code and checks for syntax errors.
+ *
+ * @param code The code to check
+ * @return true if there were no syntax errors in the code, otherwise false
+ */
+ bool checkSyntax(const UString &code);
+
+ /**
+ * Evaluates the supplied ECMAScript code.
+ *
+ * Since this method returns a Completion, you should check the type of
+ * completion to detect an error or before attempting to access the returned
+ * value. For example, if an error occurs during script execution and is not
+ * caught by the script, the completion type will be Throw.
+ *
+ * If the supplied code is invalid, a SyntaxError will be thrown.
+ *
+ * @param code The code to evaluate
+ * @param thisV The value to pass in as the "this" value for the script
+ * execution. This should either be Null() or an Object.
+ * @return A completion object representing the result of the execution.
+ */
+ Completion evaluate(const UString &code, const Value &thisV = Value());
+
+ /**
+ * @internal
+ *
+ * Returns the implementation object associated with this interpreter.
+ * Only useful for internal KJS operations.
+ */
+ InterpreterImp *imp();
+
+ /**
+ * Returns the builtin "Object" object. This is the object that was set
+ * as a property of the global object during construction; if the property
+ * is replaced by script code, this method will still return the original
+ * object.
+ *
+ * @return The builtin "Object" object
+ */
+ Object builtinObject() const;
+
+ /**
+ * Returns the builtin "Function" object.
+ */
+ Object builtinFunction() const;
+
+ /**
+ * Returns the builtin "Array" object.
+ */
+ Object builtinArray() const;
+
+ /**
+ * Returns the builtin "Boolean" object.
+ */
+ Object builtinBoolean() const;
+
+ /**
+ * Returns the builtin "String" object.
+ */
+ Object builtinString() const;
+
+ /**
+ * Returns the builtin "Number" object.
+ */
+ Object builtinNumber() const;
+
+ /**
+ * Returns the builtin "Date" object.
+ */
+ Object builtinDate() const;
+
+ /**
+ * Returns the builtin "RegExp" object.
+ */
+ Object builtinRegExp() const;
+
+ /**
+ * Returns the builtin "Error" object.
+ */
+ Object builtinError() const;
+
+ /**
+ * Returns the builtin "Object.prototype" object.
+ */
+ Object builtinObjectPrototype() const;
+
+ /**
+ * Returns the builtin "Function.prototype" object.
+ */
+ Object builtinFunctionPrototype() const;
+
+ /**
+ * Returns the builtin "Array.prototype" object.
+ */
+ Object builtinArrayPrototype() const;
+
+ /**
+ * Returns the builtin "Boolean.prototype" object.
+ */
+ Object builtinBooleanPrototype() const;
+
+ /**
+ * Returns the builtin "String.prototype" object.
+ */
+ Object builtinStringPrototype() const;
+
+ /**
+ * Returns the builtin "Number.prototype" object.
+ */
+ Object builtinNumberPrototype() const;
+
+ /**
+ * Returns the builtin "Date.prototype" object.
+ */
+ Object builtinDatePrototype() const;
+
+ /**
+ * Returns the builtin "RegExp.prototype" object.
+ */
+ Object builtinRegExpPrototype() const;
+
+ /**
+ * Returns the builtin "Error.prototype" object.
+ */
+ Object builtinErrorPrototype() const;
+
+ /**
+ * The initial value of "Error" global property
+ */
+ Object builtinEvalError() const;
+ Object builtinRangeError() const;
+ Object builtinReferenceError() const;
+ Object builtinSyntaxError() const;
+ Object builtinTypeError() const;
+ Object builtinURIError() const;
+
+ Object builtinEvalErrorPrototype() const;
+ Object builtinRangeErrorPrototype() const;
+ Object builtinReferenceErrorPrototype() const;
+ Object builtinSyntaxErrorPrototype() const;
+ Object builtinTypeErrorPrototype() const;
+ Object builtinURIErrorPrototype() const;
+
+ enum CompatMode { NativeMode, IECompat, NetscapeCompat };
+ /**
+ * Call this to enable a compatibility mode with another browser.
+ * (by default konqueror is in "native mode").
+ * Currently, in KJS, this only changes the behavior of Date::getYear()
+ * which returns the full year under IE.
+ */
+ void setCompatMode(CompatMode mode);
+ CompatMode compatMode() const;
+
+ /**
+ * Run the garbage collection. Returns true when at least one object
+ * was collected; false otherwise.
+ */
+ static bool collect();
+
+ /**
+ * Called by InterpreterImp during the mark phase of the garbage collector
+ * Default implementation does nothing, this exist for classes that reimplement Interpreter.
+ */
+ virtual void mark() {}
+
+ /**
+ * Provides a way to distinguish derived classes.
+ * Only useful if you reimplement Interpreter and if different kind of
+ * interpreters are created in the same process.
+ * The base class returns 0, the ECMA-bindings interpreter returns 1.
+ */
+ virtual int rtti() { return 0; }
+
+#ifdef KJS_DEBUG_MEM
+ /**
+ * @internal
+ */
+ static void finalCheck();
+#endif
+ private:
+ InterpreterImp *rep;
+
+ /**
+ * This constructor is not implemented, in order to prevent
+ * copy-construction of Interpreter objects. You should always pass around
+ * pointers to an interpreter instance instead.
+ */
+ Interpreter(const Interpreter&);
+
+ /**
+ * This constructor is not implemented, in order to prevent assignment of
+ * Interpreter objects. You should always pass around pointers to an
+ * interpreter instance instead.
+ */
+ Interpreter operator=(const Interpreter&);
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ };
+
+ /**
+ * Represents the current state of script execution. This object allows you
+ * obtain a handle the interpreter that is currently executing the script,
+ * and also the current execution state context.
+ */
+ class KJS_EXPORT ExecState {
+ friend class InterpreterImp;
+ friend class FunctionImp;
+ friend class GlobalFuncImp;
+ friend class TryNode;
+ friend class VarDeclNode;
+ friend class FuncDeclNode;
+ public:
+ /**
+ * Returns the interpreter associated with this execution state
+ *
+ * @return The interpreter executing the script
+ */
+ // ### make non-const or provide an overload pair
+ Interpreter *dynamicInterpreter() const { return _interpreter; }
+
+ // for compatibility
+ Interpreter *interpreter() const { return dynamicInterpreter(); }
+
+ /**
+ * Returns the interpreter associated with the current scope's
+ * global object
+ *
+ * @return The interpreter currently in scope
+ */
+ Interpreter *lexicalInterpreter() const;
+
+ /**
+ * Returns the execution context associated with this execution state
+ *
+ * @return The current execution state context
+ */
+ Context context() const { return _context; }
+
+ void setException(const Value &e);
+ void clearException();
+ Value exception() const { return _exception; }
+ // ### make const
+ bool hadException();
+
+ /*
+ * request for ending execution with an exception
+ */
+ static void requestTerminate() { terminate_request = true; }
+ /*
+ * optional confirmation for ending execution after requestTerminate()
+ */
+ static bool (*confirmTerminate)();
+ private:
+ ExecState(Interpreter *interp, ContextImp *con)
+ : _interpreter(interp), _context(con) { }
+ Interpreter *_interpreter;
+ ContextImp *_context;
+ Value _exception;
+ static bool terminate_request;
+ };
+
+} // namespace
+
+#endif // _KJS_INTERPRETER_H_
diff --git a/kjs/keywords.table b/kjs/keywords.table
new file mode 100644
index 000000000..70fb8647d
--- /dev/null
+++ b/kjs/keywords.table
@@ -0,0 +1,70 @@
+# main keywords
+@begin mainTable 44
+# types
+null NULLTOKEN
+true TRUETOKEN
+false FALSETOKEN
+# keywords
+break BREAK
+case CASE
+catch CATCH
+const CONST
+default DEFAULT
+finally FINALLY
+for FOR
+instanceof INSTANCEOF
+new NEW
+var VAR
+continue CONTINUE
+function FUNCTION
+return RETURN
+void VOID
+delete DELETE
+if IF
+this THIS
+do DO
+while WHILE
+else ELSE
+in IN
+switch SWITCH
+throw THROW
+try TRY
+typeof TYPEOF
+with WITH
+debugger DEBUGGER
+# reserved for future use
+enum RESERVED
+export RESERVED
+extends RESERVED
+import RESERVED
+super RESERVED
+# All of the following are reserved for future use as per
+# ECMA-262, but are permitted as identifiers by both of
+# the widespread browsers.
+#
+#abstract RESERVED
+#boolean RESERVED
+#byte RESERVED
+#char RESERVED
+#class RESERVED
+#double RESERVED
+#final RESERVED
+#float RESERVED
+#goto RESERVED
+#implements RESERVED
+#int RESERVED
+#interface RESERVED
+#long RESERVED
+#native RESERVED
+#package RESERVED
+#private RESERVED
+#protected RESERVED
+#public RESERVED
+#short RESERVED
+#static RESERVED
+#synchronized RESERVED
+#throws RESERVED
+#transient RESERVED
+#volatile RESERVED
+@end
+
diff --git a/kjs/kjs-devel-gdb b/kjs/kjs-devel-gdb
new file mode 100644
index 000000000..41725379f
--- /dev/null
+++ b/kjs/kjs-devel-gdb
@@ -0,0 +1,22 @@
+# This file defines handy gdb macros
+# To use it, add this line to your ~/.gdbinit :
+# source /path/to/kde/sources/kdelibs/kjs/kjs-devel-gdb
+
+define printucharstar
+ set $i=0
+ while ($i<($arg1))
+ p (char)(($arg0)[$i++])
+ end
+end
+document printucharstar
+ Prints the contents of an UChar [] - for KJS.
+ Usage: 'printucharstar <UChar* dat> <int len>'
+end
+
+define printustring
+ printucharstar ($arg0).rep->dat ($arg0).rep->len
+end
+document printustring
+ Prints the contents of an UString - for KJS
+ Usage: 'printustring <UString str>'
+end
diff --git a/kjs/lexer.cpp b/kjs/lexer.cpp
new file mode 100644
index 000000000..054defb88
--- /dev/null
+++ b/kjs/lexer.cpp
@@ -0,0 +1,930 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "nodes.h"
+#include "lexer.h"
+#include "identifier.h"
+#include "lookup.h"
+#include "internal.h"
+#include "dtoa.h"
+
+// we can't specify the namespace in yacc's C output, so do it here
+using namespace KJS;
+
+static Lexer *currLexer = 0;
+
+#ifndef KDE_USE_FINAL
+#include "grammar.h"
+#endif
+
+#include "lexer.lut.h"
+
+extern YYLTYPE yylloc; // global bison variable holding token info
+
+// a bridge for yacc from the C world to C++
+int kjsyylex()
+{
+ return Lexer::curr()->lex();
+}
+
+Lexer::Lexer()
+ : yylineno(1),
+ size8(128), size16(128), restrKeyword(false),
+ convertNextIdentifier(false), stackToken(-1), lastToken(-1), pos(0),
+ code(0), length(0),
+#ifndef KJS_PURE_ECMA
+ bol(true),
+#endif
+ current(0), next1(0), next2(0), next3(0),
+ strings(0), numStrings(0), stringsCapacity(0),
+ identifiers(0), numIdentifiers(0), identifiersCapacity(0)
+{
+ // allocate space for read buffers
+ buffer8 = new char[size8];
+ buffer16 = new UChar[size16];
+ currLexer = this;
+}
+
+Lexer::~Lexer()
+{
+ delete [] buffer8;
+ delete [] buffer16;
+}
+
+Lexer *Lexer::curr()
+{
+ if (!currLexer) {
+ // create singleton instance
+ currLexer = new Lexer();
+ }
+ return currLexer;
+}
+
+#ifdef KJS_DEBUG_MEM
+void Lexer::globalClear()
+{
+ delete currLexer;
+ currLexer = 0L;
+}
+#endif
+
+void Lexer::setCode(const UChar *c, unsigned int len)
+{
+ yylineno = 1;
+ restrKeyword = false;
+ delimited = false;
+ convertNextIdentifier = false;
+ stackToken = -1;
+ lastToken = -1;
+ foundBad = false;
+ pos = 0;
+ code = c;
+ length = len;
+ skipLF = false;
+ skipCR = false;
+#ifndef KJS_PURE_ECMA
+ bol = true;
+#endif
+
+ // read first characters
+ current = (length > 0) ? code[0].uc : -1;
+ next1 = (length > 1) ? code[1].uc : -1;
+ next2 = (length > 2) ? code[2].uc : -1;
+ next3 = (length > 3) ? code[3].uc : -1;
+}
+
+void Lexer::shift(unsigned int p)
+{
+ while (p--) {
+ pos++;
+ current = next1;
+ next1 = next2;
+ next2 = next3;
+ next3 = (pos + 3 < length) ? code[pos+3].uc : -1;
+ }
+}
+
+// called on each new line
+void Lexer::nextLine()
+{
+ yylineno++;
+#ifndef KJS_PURE_ECMA
+ bol = true;
+#endif
+}
+
+void Lexer::setDone(State s)
+{
+ state = s;
+ done = true;
+}
+
+int Lexer::lex()
+{
+ int token = 0;
+ state = Start;
+ unsigned short stringType = 0; // either single or double quotes
+ pos8 = pos16 = 0;
+ 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);
+ }
+
+ bool cr = (current == '\r');
+ bool lf = (current == '\n');
+ if (cr)
+ skipLF = true;
+ else if (lf)
+ skipCR = true;
+ bool isLineTerminator = cr || lf;
+
+ switch (state) {
+ case Start:
+ if (isWhiteSpace(current)) {
+ // 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 = current;
+ } else if (isIdentLetter(current)) {
+ record16(current);
+ state = InIdentifierOrKeyword;
+ } else if (current == '\\') {
+ state = InIdentifierUnicodeEscapeStart;
+ } 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;
+#ifndef KJS_PURE_ECMA
+ // <!-- marks the beginning of a line comment (for www usage)
+ } else if (current == '<' && next1 == '!' &&
+ next2 == '-' && next3 == '-') {
+ shift(3);
+ state = InSingleLineComment;
+ // same for -->
+ } else if (bol && current == '-' && next1 == '-' && next2 == '>') {
+ shift(2);
+ state = InSingleLineComment;
+#endif
+ } else {
+ token = matchPunctuator(current, next1, next2, next3);
+ if (token != -1) {
+ setDone(Other);
+ } else {
+ // cerr << "encountered unknown character" << endl;
+ setDone(Bad);
+ }
+ }
+ break;
+ case InString:
+ if (current == stringType) {
+ shift(1);
+ setDone(String);
+ } else if (current == -1 || isLineTerminator) {
+ setDone(Bad);
+ } else if (current == '\\') {
+ state = InEscapeSequence;
+ } else {
+ record16(current);
+ }
+ break;
+ // Escape Sequences inside of strings
+ case InEscapeSequence:
+ if (isOctalDigit(current)) {
+ if (current >= '0' && current <= '3' &&
+ isOctalDigit(next1) && isOctalDigit(next2)) {
+ record16(convertOctal(current, next1, next2));
+ shift(2);
+ state = InString;
+ } else if (isOctalDigit(current) && isOctalDigit(next1)) {
+ record16(convertOctal('0', current, next1));
+ shift(1);
+ state = InString;
+ } else if (isOctalDigit(current)) {
+ record16(convertOctal('0', '0', current));
+ state = InString;
+ } else {
+ setDone(Bad);
+ }
+ } else if (current == 'x')
+ state = InHexEscape;
+ else if (current == 'u')
+ state = InUnicodeEscape;
+ else {
+ if (isLineTerminator)
+ nextLine();
+ record16(singleEscape(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 (isIdentLetter(current) || isDecimalDigit(current))
+ record16(current);
+ else if (current == '\\')
+ state = InIdentifierUnicodeEscapeStart;
+ 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 InIdentifierUnicodeEscapeStart:
+ if (current == 'u')
+ state = InIdentifierUnicodeEscape;
+ else
+ setDone(Bad);
+ break;
+ case InIdentifierUnicodeEscape:
+ if (isHexDigit(current) && isHexDigit(next1) && isHexDigit(next2) && isHexDigit(next3)) {
+ record16(convertUnicode(current, next1, next2, next3));
+ shift(3);
+ state = InIdentifier;
+ } else {
+ setDone(Bad);
+ }
+ break;
+ default:
+ assert(!"Unhandled state in switch statement");
+ }
+
+ // move on to the next character
+ if (!done)
+ shift(1);
+#ifndef KJS_PURE_ECMA
+ if (state != Start && state != InSingleLineComment)
+ bol = false;
+#endif
+ }
+
+ // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
+ if ((state == Number || state == Octal || state == Hex)
+ && isIdentLetter(current))
+ state = Bad;
+
+ // terminate string
+ buffer8[pos8] = '\0';
+
+#ifdef KJS_DEBUG_LEX
+ fprintf(stderr, "line: %d ", lineNo());
+ fprintf(stderr, "yytext (%x): ", buffer8[0]);
+ fprintf(stderr, "%s ", buffer8);
+#endif
+
+ long double dval = 0;
+ if (state == Number) {
+ dval = kjs_strtod(buffer8, 0L);
+ } else if (state == Hex) { // scan hex numbers
+ dval = 0;
+ if (buffer8[0] == '0' && (buffer8[1] == 'x' || buffer8[1] == 'X')) {
+ for (const char *p = buffer8+2; *p; p++) {
+ if (!isHexDigit(*p)) {
+ dval = 0;
+ break;
+ }
+ dval = dval * 16 + convertHex(*p);
+ }
+ }
+ state = Number;
+ } else if (state == Octal) { // scan octal number
+ dval = 0;
+ if (buffer8[0] == '0') {
+ for (const char *p = buffer8+1; *p; p++) {
+ if (*p < '0' || *p > '7') {
+ dval = 0;
+ break;
+ }
+ dval = dval * 8 + *p - '0';
+ }
+ }
+ state = Number;
+ }
+
+#ifdef KJS_DEBUG_LEX
+ switch (state) {
+ case Eof:
+ printf("(EOF)\n");
+ break;
+ case Other:
+ printf("(Other)\n");
+ break;
+ case Identifier:
+ case IdentifierOrKeyword:
+ printf("(Identifier)/(Keyword)\n");
+ break;
+ case String:
+ printf("(String)\n");
+ break;
+ case Number:
+ printf("(Number)\n");
+ break;
+ default:
+ printf("(unknown)");
+ }
+#endif
+
+ if (state != Identifier && state != IdentifierOrKeyword &&
+ convertNextIdentifier)
+ convertNextIdentifier = 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, buffer16, pos16)) < 0) {
+ case Identifier:
+ // Lookup for keyword failed, means this is an identifier
+ // Apply anonymous-function hack below (convert the identifier)
+ if (convertNextIdentifier) {
+ convertNextIdentifier = false;
+#ifdef KJS_VERBOSE
+ UString debugstr(buffer16, pos16); fprintf(stderr,"Anonymous function hack: eating identifier %s\n",debugstr.ascii());
+#endif
+ token = FUNCEXPRIDENT;
+ } else {
+ token = IDENT;
+ }
+ /* TODO: close leak on parse error. same holds true for String */
+ kjsyylval.ident = makeIdentifier(buffer16, pos16);
+ break;
+ }
+
+ convertNextIdentifier = false;
+ // Hack for "f = function somename() { ... }", too hard to get into the grammar
+ // Same for building an array with function pointers ( 'name', func1, 'name2', func2 )
+ // There are lots of other uses, we really have to get this into the grammar
+ if ( token == FUNCTION &&
+ ( lastToken == '=' || lastToken == ',' || lastToken == '(' ||
+ lastToken == ':' || lastToken == RETURN ) )
+ convertNextIdentifier = true;
+
+ if (token == CONTINUE || token == BREAK ||
+ token == RETURN || token == THROW)
+ restrKeyword = true;
+ break;
+ case String:
+ kjsyylval.ustr = makeUString(buffer16, pos16);
+ token = STRING;
+ break;
+ case Number:
+ kjsyylval.dval = dval;
+ token = NUMBER;
+ break;
+ case Bad:
+ foundBad = true;
+ return -1;
+ default:
+ assert(!"unhandled numeration value in switch");
+ return -1;
+ }
+ lastToken = token;
+ return token;
+}
+
+bool Lexer::isWhiteSpace(unsigned short c)
+{
+ return (c == ' ' || c == '\t' ||
+ c == 0x0b || c == 0x0c || c == 0xa0);
+}
+
+bool Lexer::isIdentLetter(unsigned short c)
+{
+ // Allow any character in the Unicode categories
+ // Uppercase letter (Lu), Lowercase letter (Ll),
+ // Titlecase letter (Lt)", Modifier letter (Lm),
+ // Other letter (Lo), or Letter number (Nl).
+ // Also see: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt */
+ return (c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ // A with grave - O with diaeresis
+ c >= 0x00c0 && c <= 0x00d6 ||
+ // O with stroke - o with diaeresis
+ c >= 0x00d8 && c <= 0x00f6 ||
+ // o with stroke - turned h with fishook and tail
+ c >= 0x00f8 && c <= 0x02af ||
+ // Greek etc. TODO: not precise
+ c >= 0x0388 && c <= 0x1ffc ||
+ c == '$' || c == '_');
+ /* TODO: use complete category table */
+}
+
+bool Lexer::isDecimalDigit(unsigned short c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+bool Lexer::isHexDigit(unsigned short c)
+{
+ return (c >= '0' && c <= '9' ||
+ c >= 'a' && c <= 'f' ||
+ c >= 'A' && c <= 'F');
+}
+
+bool Lexer::isOctalDigit(unsigned short c)
+{
+ return (c >= '0' && c <= '7');
+}
+
+int Lexer::matchPunctuator(unsigned short c1, unsigned short c2,
+ unsigned short c3, unsigned short c4)
+{
+ if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') {
+ shift(4);
+ return URSHIFTEQUAL;
+ } else if (c1 == '=' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return STREQ;
+ } else if (c1 == '!' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return STRNEQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '>') {
+ shift(3);
+ return URSHIFT;
+ } else if (c1 == '<' && c2 == '<' && c3 == '=') {
+ shift(3);
+ return LSHIFTEQUAL;
+ } else if (c1 == '>' && c2 == '>' && c3 == '=') {
+ shift(3);
+ return RSHIFTEQUAL;
+ } else if (c1 == '<' && c2 == '=') {
+ shift(2);
+ return LE;
+ } else if (c1 == '>' && c2 == '=') {
+ shift(2);
+ return GE;
+ } else if (c1 == '!' && c2 == '=') {
+ shift(2);
+ return NE;
+ } else if (c1 == '+' && c2 == '+') {
+ shift(2);
+ if (terminator)
+ return AUTOPLUSPLUS;
+ else
+ return PLUSPLUS;
+ } else if (c1 == '-' && c2 == '-') {
+ shift(2);
+ if (terminator)
+ return AUTOMINUSMINUS;
+ else
+ return MINUSMINUS;
+ } else if (c1 == '=' && c2 == '=') {
+ shift(2);
+ return EQEQ;
+ } else if (c1 == '+' && c2 == '=') {
+ shift(2);
+ return PLUSEQUAL;
+ } else if (c1 == '-' && c2 == '=') {
+ shift(2);
+ return MINUSEQUAL;
+ } else if (c1 == '*' && c2 == '=') {
+ shift(2);
+ return MULTEQUAL;
+ } else if (c1 == '/' && c2 == '=') {
+ shift(2);
+ return DIVEQUAL;
+ } else if (c1 == '&' && c2 == '=') {
+ shift(2);
+ return ANDEQUAL;
+ } else if (c1 == '^' && c2 == '=') {
+ shift(2);
+ return XOREQUAL;
+ } else if (c1 == '%' && c2 == '=') {
+ shift(2);
+ return MODEQUAL;
+ } else if (c1 == '|' && c2 == '=') {
+ shift(2);
+ return OREQUAL;
+ } else if (c1 == '<' && c2 == '<') {
+ shift(2);
+ return LSHIFT;
+ } else if (c1 == '>' && c2 == '>') {
+ shift(2);
+ return RSHIFT;
+ } else if (c1 == '&' && c2 == '&') {
+ shift(2);
+ return AND;
+ } else if (c1 == '|' && c2 == '|') {
+ shift(2);
+ return OR;
+ }
+
+ switch(c1) {
+ case '=':
+ case '>':
+ case '<':
+ case ',':
+ case '!':
+ case '~':
+ case '?':
+ case ':':
+ case '.':
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '&':
+ case '|':
+ case '^':
+ case '%':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ';':
+ shift(1);
+ return static_cast<int>(c1);
+ default:
+ return -1;
+ }
+}
+
+unsigned short Lexer::singleEscape(unsigned short c) const
+{
+ 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(unsigned short c1, unsigned short c2,
+ unsigned short c3) const
+{
+ return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0');
+}
+
+unsigned char Lexer::convertHex(unsigned short c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (c - 'A' + 10);
+}
+
+unsigned char Lexer::convertHex(unsigned short c1, unsigned short c2)
+{
+ return ((convertHex(c1) << 4) + convertHex(c2));
+}
+
+UChar Lexer::convertUnicode(unsigned short c1, unsigned short c2,
+ unsigned short c3, unsigned short c4)
+{
+ return UChar((convertHex(c1) << 4) + convertHex(c2),
+ (convertHex(c3) << 4) + convertHex(c4));
+}
+
+void Lexer::record8(unsigned short c)
+{
+ assert(c <= 0xff);
+
+ // enlarge buffer if full
+ if (pos8 >= size8 - 1) {
+ char *tmp = new char[2 * size8];
+ memcpy(tmp, buffer8, size8 * sizeof(char));
+ delete [] buffer8;
+ buffer8 = tmp;
+ size8 *= 2;
+ }
+
+ buffer8[pos8++] = (char) c;
+}
+
+void Lexer::record16(int c)
+{
+ assert(c >= 0);
+ //assert(c <= USHRT_MAX);
+ record16(UChar(static_cast<unsigned short>(c)));
+}
+
+void Lexer::record16(UChar c)
+{
+ // enlarge buffer if full
+ if (pos16 >= size16 - 1) {
+ UChar *tmp = new UChar[2 * size16];
+ memcpy(tmp, buffer16, size16 * sizeof(UChar));
+ delete [] buffer16;
+ buffer16 = tmp;
+ size16 *= 2;
+ }
+
+ buffer16[pos16++] = c;
+}
+
+bool Lexer::scanRegExp()
+{
+ pos16 = 0;
+ bool lastWasEscape = false;
+ bool inBrackets = false;
+
+ while (1) {
+ if (current == '\r' || current == '\n' || current == -1)
+ return false;
+ else if (current != '/' || lastWasEscape == true || inBrackets == true)
+ {
+ // keep track of '[' and ']'
+ if ( !lastWasEscape ) {
+ if ( current == '[' && !inBrackets )
+ inBrackets = true;
+ if ( current == ']' && inBrackets )
+ inBrackets = false;
+ }
+ record16(current);
+ lastWasEscape =
+ !lastWasEscape && (current == '\\');
+ }
+ else { // end of regexp
+ pattern = UString(buffer16, pos16);
+ pos16 = 0;
+ shift(1);
+ break;
+ }
+ shift(1);
+ }
+
+ while (isIdentLetter(current)) {
+ record16(current);
+ shift(1);
+ }
+ flags = UString(buffer16, pos16);
+
+ return true;
+}
+
+
+void Lexer::doneParsing()
+{
+ for (unsigned i = 0; i < numIdentifiers; i++) {
+ delete identifiers[i];
+ }
+ free(identifiers);
+ identifiers = 0;
+ numIdentifiers = 0;
+ identifiersCapacity = 0;
+
+ for (unsigned i = 0; i < numStrings; i++) {
+ delete strings[i];
+ }
+ free(strings);
+ strings = 0;
+ numStrings = 0;
+ stringsCapacity = 0;
+}
+
+const int initialCapacity = 64;
+const int growthFactor = 2;
+
+Identifier *Lexer::makeIdentifier(UChar *buffer, unsigned int pos)
+{
+ if (numIdentifiers == identifiersCapacity) {
+ identifiersCapacity = (identifiersCapacity == 0) ? initialCapacity : identifiersCapacity *growthFactor;
+ identifiers = (KJS::Identifier **)realloc(identifiers, sizeof(KJS::Identifier *) * identifiersCapacity);
+ }
+
+ KJS::Identifier *identifier = new KJS::Identifier(buffer, pos);
+ identifiers[numIdentifiers++] = identifier;
+ return identifier;
+}
+
+UString *Lexer::makeUString(UChar *buffer, unsigned int pos)
+{
+ if (numStrings == stringsCapacity) {
+ stringsCapacity = (stringsCapacity == 0) ? initialCapacity : stringsCapacity *growthFactor;
+ strings = (UString **)realloc(strings, sizeof(UString *) * stringsCapacity);
+ }
+
+ UString *string = new UString(buffer, pos);
+ strings[numStrings++] = string;
+ return string;
+}
diff --git a/kjs/lexer.h b/kjs/lexer.h
new file mode 100644
index 000000000..c6cb6aa88
--- /dev/null
+++ b/kjs/lexer.h
@@ -0,0 +1,165 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJSLEXER_H_
+#define _KJSLEXER_H_
+
+#include "ustring.h"
+
+
+namespace KJS {
+
+ class Identifier;
+
+ class RegExp;
+
+ class Lexer {
+ public:
+ Lexer();
+ ~Lexer();
+ static Lexer *curr();
+
+ void setCode(const UChar *c, unsigned int len);
+ int lex();
+
+ int lineNo() const { return yylineno + 1; }
+
+ bool prevTerminator() const { return terminator; }
+
+ enum State { Start,
+ IdentifierOrKeyword,
+ Identifier,
+ InIdentifierOrKeyword,
+ InIdentifier,
+ InIdentifierUnicodeEscapeStart,
+ InIdentifierUnicodeEscape,
+ InSingleLineComment,
+ InMultiLineComment,
+ InNum,
+ InNum0,
+ InHex,
+ InOctal,
+ InDecimal,
+ InExponentIndicator,
+ InExponent,
+ Hex,
+ Octal,
+ Number,
+ String,
+ Eof,
+ InString,
+ InEscapeSequence,
+ InHexEscape,
+ InUnicodeEscape,
+ Other,
+ Bad };
+
+ bool scanRegExp();
+ UString pattern, flags;
+ bool hadError() const { return foundBad; }
+
+ static bool isWhiteSpace(unsigned short c);
+ static bool isIdentLetter(unsigned short c);
+ static bool isDecimalDigit(unsigned short c);
+ static bool isHexDigit(unsigned short c);
+ static bool isOctalDigit(unsigned short c);
+
+ private:
+ int yylineno;
+ bool done;
+ char *buffer8;
+ UChar *buffer16;
+ unsigned int size8, size16;
+ unsigned int pos8, pos16;
+ bool terminator;
+ bool restrKeyword;
+ // encountered delimiter like "'" and "}" on last run
+ bool delimited;
+ bool skipLF;
+ bool skipCR;
+ bool convertNextIdentifier;
+ int stackToken;
+ int lastToken;
+ bool foundBad;
+
+ State state;
+ void setDone(State s);
+ unsigned int pos;
+ void shift(unsigned int p);
+ void nextLine();
+ int lookupKeyword(const char *);
+
+ int matchPunctuator(unsigned short c1, unsigned short c2,
+ unsigned short c3, unsigned short c4);
+ unsigned short singleEscape(unsigned short c) const;
+ unsigned short convertOctal(unsigned short c1, unsigned short c2,
+ unsigned short c3) const;
+ public:
+ static unsigned char convertHex(unsigned short c1);
+ static unsigned char convertHex(unsigned short c1, unsigned short c2);
+ static UChar convertUnicode(unsigned short c1, unsigned short c2,
+ unsigned short c3, unsigned short c4);
+
+#ifdef KJS_DEBUG_MEM
+ /**
+ * Clear statically allocated resources
+ */
+ static void globalClear();
+#endif
+
+ void doneParsing();
+
+ private:
+
+ void record8(unsigned short c);
+ void record16(int c);
+ void record16(UChar c);
+
+ KJS::Identifier *makeIdentifier(UChar *buffer, unsigned int pos);
+ UString *makeUString(UChar *buffer, unsigned int pos);
+
+ const UChar *code;
+ unsigned int length;
+ int yycolumn;
+#ifndef KJS_PURE_ECMA
+ int bol; // begin of line
+#endif
+
+ // current and following unicode characters (int to allow for -1 for end-of-file marker)
+ int current, next1, next2, next3;
+
+ UString **strings;
+ unsigned int numStrings;
+ unsigned int stringsCapacity;
+
+ KJS::Identifier **identifiers;
+ unsigned int numIdentifiers;
+ unsigned int identifiersCapacity;
+
+ // for future extensions
+ class LexerPrivate;
+ LexerPrivate *priv;
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/libkjs.map b/kjs/libkjs.map
new file mode 100644
index 000000000..4c490a3c5
--- /dev/null
+++ b/kjs/libkjs.map
@@ -0,0 +1,30 @@
+{
+ local:
+ extern "C++"
+ {
+ kjsyy*;
+ yylloc;
+ KJS::*Node*::*;
+ vtable?for?KJS::*Node*;
+ typeinfo*KJS::*Node*;
+ # vtable and typeinfo symbols not matched below
+ KJS::Lexer::*;
+ KJS::Parser::*;
+ KJS::Collector::*;
+ KJS::InterpreterImp::*;
+ KJS::ListImp*;
+ *KJS::*InstanceImp*;
+ KJS::CompletionImp*;
+ KJS::StringObjectImp*;
+ KJS::BooleanObjectImp*;
+ KJS::FunctionObjectImp*;
+ KJS::DateObjectImp*;
+ KJS::MathObjectImp*;
+ KJS::ArrayObjectImp*;
+ KJS::NumberObjectImp*;
+ KJS::RegExpObjectImp*;
+ KJS::ErrorObjectImp*;
+ KJS::ObjectObjectImp*;
+ KJS::*ProtoFuncIm*
+ };
+};
diff --git a/kjs/list.cpp b/kjs/list.cpp
new file mode 100644
index 000000000..7c674573b
--- /dev/null
+++ b/kjs/list.cpp
@@ -0,0 +1,328 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "list.h"
+
+#include "internal.h"
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define DUMP_STATISTICS 0
+
+namespace KJS {
+
+// tunable parameters
+const int poolSize = 32; // must be a power of 2
+const int inlineValuesSize = 4;
+
+// derived constants
+const int poolSizeMask = poolSize - 1;
+
+enum ListImpState { unusedInPool = 0, usedInPool, usedOnHeap, immortal };
+
+struct ListImp : ListImpBase
+{
+ ListImpState state;
+ ValueImp *values[inlineValuesSize];
+ int capacity;
+ ValueImp **overflow;
+
+#if DUMP_STATISTICS
+ int sizeHighWaterMark;
+#endif
+};
+
+static ListImp pool[poolSize];
+static int poolCursor;
+
+#if DUMP_STATISTICS
+
+static int numLists;
+static int numListsHighWaterMark;
+
+static int listSizeHighWaterMark;
+
+static int numListsDestroyed;
+static int numListsBiggerThan[17];
+
+struct ListStatisticsExitLogger { ~ListStatisticsExitLogger(); };
+
+static ListStatisticsExitLogger logger;
+
+ListStatisticsExitLogger::~ListStatisticsExitLogger()
+{
+ printf("\nKJS::List statistics:\n\n");
+ printf("%d lists were allocated\n", numLists);
+ printf("%d lists was the high water mark\n", numListsHighWaterMark);
+ printf("largest list had %d elements\n", listSizeHighWaterMark);
+ if (numListsDestroyed) {
+ putc('\n', stdout);
+ for (int i = 0; i < 17; i++) {
+ printf("%.1f%% of the lists (%d) had more than %d element%s\n",
+ 100.0 * numListsBiggerThan[i] / numListsDestroyed,
+ numListsBiggerThan[i],
+ i, i == 1 ? "" : "s");
+ }
+ putc('\n', stdout);
+ }
+}
+
+#endif
+
+static inline ListImp *allocateListImp()
+{
+ // Find a free one in the pool.
+ int c = poolCursor;
+ int i = c;
+ do {
+ ListImp *imp = &pool[i];
+ ListImpState s = imp->state;
+ i = (i + 1) & poolSizeMask;
+ if (s == unusedInPool) {
+ poolCursor = i;
+ imp->state = usedInPool;
+ return imp;
+ }
+ } while (i != c);
+
+ ListImp *imp = new ListImp;
+ imp->state = usedOnHeap;
+ return imp;
+}
+
+static inline void deallocateListImp(ListImp *imp)
+{
+ if (imp->state == usedInPool)
+ imp->state = unusedInPool;
+ else
+ delete imp;
+}
+
+List::List() : _impBase(allocateListImp()), _needsMarking(false)
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+ imp->size = 0;
+ imp->refCount = 1;
+ imp->capacity = 0;
+ imp->overflow = 0;
+
+ if (!_needsMarking) {
+ imp->valueRefCount = 1;
+ }
+#if DUMP_STATISTICS
+ if (++numLists > numListsHighWaterMark)
+ numListsHighWaterMark = numLists;
+ imp->sizeHighWaterMark = 0;
+#endif
+}
+
+List::List(bool needsMarking) : _impBase(allocateListImp()), _needsMarking(needsMarking)
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+ imp->size = 0;
+ imp->refCount = 1;
+ imp->capacity = 0;
+ imp->overflow = 0;
+
+ if (!_needsMarking) {
+ imp->valueRefCount = 1;
+ }
+
+#if DUMP_STATISTICS
+ if (++numLists > numListsHighWaterMark)
+ numListsHighWaterMark = numLists;
+ imp->sizeHighWaterMark = 0;
+#endif
+}
+
+void List::derefValues()
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+ int size = imp->size;
+
+ int inlineSize = MIN(size, inlineValuesSize);
+ for (int i = 0; i != inlineSize; ++i)
+ imp->values[i]->deref();
+
+ int overflowSize = size - inlineSize;
+ ValueImp **overflow = imp->overflow;
+ for (int i = 0; i != overflowSize; ++i)
+ overflow[i]->deref();
+}
+
+void List::refValues()
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+ int size = imp->size;
+
+ int inlineSize = MIN(size, inlineValuesSize);
+ for (int i = 0; i != inlineSize; ++i)
+ imp->values[i]->ref();
+
+ int overflowSize = size - inlineSize;
+ ValueImp **overflow = imp->overflow;
+ for (int i = 0; i != overflowSize; ++i)
+ overflow[i]->ref();
+}
+
+void List::markValues()
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+ int size = imp->size;
+
+ int inlineSize = MIN(size, inlineValuesSize);
+ for (int i = 0; i != inlineSize; ++i) {
+ if (!imp->values[i]->marked()) {
+ imp->values[i]->mark();
+ }
+ }
+
+ int overflowSize = size - inlineSize;
+ ValueImp **overflow = imp->overflow;
+ for (int i = 0; i != overflowSize; ++i) {
+ if (!overflow[i]->marked()) {
+ overflow[i]->mark();
+ }
+ }
+}
+
+void List::release()
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+#if DUMP_STATISTICS
+ --numLists;
+ ++numListsDestroyed;
+ for (int i = 0; i < 17; i++)
+ if (imp->sizeHighWaterMark > i)
+ ++numListsBiggerThan[i];
+#endif
+
+ delete [] imp->overflow;
+ deallocateListImp(imp);
+}
+
+ValueImp *List::impAt(int i) const
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+ if ((unsigned)i >= (unsigned)imp->size)
+ return UndefinedImp::staticUndefined;
+ if (i < inlineValuesSize)
+ return imp->values[i];
+ return imp->overflow[i - inlineValuesSize];
+}
+
+void List::clear()
+{
+ if (_impBase->valueRefCount > 0) {
+ derefValues();
+ }
+ _impBase->size = 0;
+}
+
+void List::append(ValueImp *v)
+{
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+ int i = imp->size++;
+
+#if DUMP_STATISTICS
+ if (imp->size > listSizeHighWaterMark)
+ listSizeHighWaterMark = imp->size;
+#endif
+
+ if (imp->valueRefCount > 0) {
+ v->ref();
+ }
+
+ if (i < inlineValuesSize) {
+ imp->values[i] = v;
+ return;
+ }
+
+ if (i >= imp->capacity) {
+ int newCapacity = i * 2;
+ ValueImp **newOverflow = new ValueImp * [newCapacity - inlineValuesSize];
+ ValueImp **oldOverflow = imp->overflow;
+ int oldOverflowSize = i - inlineValuesSize;
+ for (int j = 0; j != oldOverflowSize; j++)
+ newOverflow[j] = oldOverflow[j];
+ delete [] oldOverflow;
+ imp->overflow = newOverflow;
+ imp->capacity = newCapacity;
+ }
+
+ imp->overflow[i - inlineValuesSize] = v;
+}
+
+List List::copy() const
+{
+ List copy;
+
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+ int size = imp->size;
+
+ int inlineSize = MIN(size, inlineValuesSize);
+ for (int i = 0; i != inlineSize; ++i)
+ copy.append(imp->values[i]);
+
+ ValueImp **overflow = imp->overflow;
+ int overflowSize = size - inlineSize;
+ for (int i = 0; i != overflowSize; ++i)
+ copy.append(overflow[i]);
+
+ return copy;
+}
+
+
+List List::copyTail() const
+{
+ List copy;
+
+ ListImp *imp = static_cast<ListImp *>(_impBase);
+
+ int size = imp->size;
+
+ int inlineSize = MIN(size, inlineValuesSize);
+ for (int i = 1; i < inlineSize; ++i)
+ copy.append(imp->values[i]);
+
+ ValueImp **overflow = imp->overflow;
+ int overflowSize = size - inlineSize;
+ for (int i = 0; i < overflowSize; ++i)
+ copy.append(overflow[i]);
+
+ return copy;
+}
+
+const List &List::empty()
+{
+ static List emptyList;
+ return emptyList;
+}
+
+} // namespace KJS
diff --git a/kjs/list.h b/kjs/list.h
new file mode 100644
index 000000000..c0d28ac72
--- /dev/null
+++ b/kjs/list.h
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * 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_LIST_H
+#define KJS_LIST_H
+
+#include "value.h"
+
+namespace KJS {
+
+ struct ListImpBase {
+ int size;
+ int refCount;
+ int valueRefCount;
+ };
+
+ class ListIterator;
+
+ /**
+ * @short Native list type.
+ *
+ * List is a native ECMAScript type. List values are only used for
+ * intermediate results of expression evaluation and cannot be stored
+ * as properties of objects.
+ *
+ * The list is explicitly shared. Note that while copyTail() returns a
+ * copy of the list the referenced objects are still shared.
+ */
+ class KJS_EXPORT List {
+ public:
+ List();
+ List(bool needsMarking);
+ ~List() { deref(); }
+
+ List(const List &b) : _impBase(b._impBase), _needsMarking(false) {
+ ++_impBase->refCount;
+ if (!_impBase->valueRefCount) refValues();
+ ++_impBase->valueRefCount;
+ }
+ List &operator=(const List &);
+
+ /**
+ * Append an object to the end of the list.
+ *
+ * @param val Pointer to object.
+ */
+ void append(const Value& val) { append(val.imp()); }
+ void append(ValueImp *val);
+ /**
+ * Remove all elements from the list.
+ */
+ void clear();
+
+ /**
+ * Make a copy of the list
+ */
+ List copy() const;
+
+ /**
+ * Make a copy of the list, omitting the first element.
+ */
+ List copyTail() const;
+
+ /**
+ * @return true if the list is empty. false otherwise.
+ */
+ bool isEmpty() const { return _impBase->size == 0; }
+ /**
+ * @return the current size of the list.
+ */
+ int size() const { return _impBase->size; }
+ /**
+ * @return A KJS::ListIterator pointing to the first element.
+ */
+ ListIterator begin() const;
+ /**
+ * @return A KJS::ListIterator pointing to the last element.
+ */
+ ListIterator end() const;
+
+ /**
+ * Retrieve an element at an indexed position. If you want to iterate
+ * trough the whole list using KJS::ListIterator will be faster.
+ *
+ * @param i List index.
+ * @return Return the element at position i. KJS::Undefined if the
+ * index is out of range.
+ */
+ Value at(int i) const { return Value(impAt(i)); }
+ /**
+ * Equivalent to at.
+ */
+ Value operator[](int i) const { return Value(impAt(i)); }
+
+ ValueImp *impAt(int i) const;
+
+ /**
+ * Returns a pointer to a static instance of an empty list. Useful if a
+ * function has a KJS::List parameter.
+ */
+ static const List &empty();
+
+ void mark() { if (_impBase->valueRefCount == 0) markValues(); }
+ private:
+ ListImpBase *_impBase;
+ bool _needsMarking;
+
+ void deref() { if (!_needsMarking && --_impBase->valueRefCount == 0) derefValues(); if (--_impBase->refCount == 0) release(); }
+
+ void release();
+ void refValues();
+ void derefValues();
+ void markValues();
+ };
+
+ /**
+ * @short Iterator for KJS::List objects.
+ */
+ class ListIterator {
+ public:
+ /**
+ * Construct an iterator that points to the first element of the list.
+ * @param l The list the iterator will operate on.
+ */
+ ListIterator(const List &l) : _list(&l), _i(0) { }
+ ListIterator(const List &l, int index) : _list(&l), _i(index) { }
+ /**
+ * Dereference the iterator.
+ * @return A pointer to the element the iterator operates on.
+ */
+ ValueImp *operator->() const { return _list->impAt(_i); }
+ Value operator*() const { return Value(_list->impAt(_i)); }
+ /**
+ * Prefix increment operator.
+ * @return The element after the increment.
+ */
+ Value operator++() { return Value(_list->impAt(++_i)); }
+ /**
+ * Postfix increment operator.
+ */
+ Value operator++(int) { return Value(_list->impAt(_i++)); }
+ /**
+ * Prefix decrement operator.
+ */
+ Value operator--() { return Value(_list->impAt(--_i)); }
+ /**
+ * Postfix decrement operator.
+ */
+ Value operator--(int) { return Value(_list->impAt(_i--)); }
+ /**
+ * Compare the iterator with another one.
+ * @return True if the two iterators operate on the same list element.
+ * False otherwise.
+ */
+ bool operator==(const ListIterator &it) const { return _i == it._i; }
+ /**
+ * Check for inequality with another iterator.
+ * @return True if the two iterators operate on different list elements.
+ */
+ bool operator!=(const ListIterator &it) const { return _i != it._i; }
+
+ private:
+ const List *_list;
+ int _i;
+ };
+
+ inline ListIterator List::begin() const { return ListIterator(*this); }
+ inline ListIterator List::end() const { return ListIterator(*this, size()); }
+
+ inline List &List::operator=(const List &b)
+ {
+ ListImpBase *bImpBase = b._impBase;
+ ++bImpBase->refCount;
+ deref();
+ _impBase = bImpBase;
+ if (!_needsMarking) {
+ if (!_impBase->valueRefCount) {
+ refValues();
+ }
+ _impBase->valueRefCount++;
+ }
+
+ return *this;
+ }
+
+ } // namespace KJS
+
+#endif // KJS_LIST_H
diff --git a/kjs/lookup.cpp b/kjs/lookup.cpp
new file mode 100644
index 000000000..899238949
--- /dev/null
+++ b/kjs/lookup.cpp
@@ -0,0 +1,114 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 <stdio.h>
+#include <string.h>
+
+#include "lookup.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+using namespace KJS;
+
+static bool keysMatch(const UChar *c, unsigned len, const char *s)
+{
+ for (unsigned i = 0; i != len; i++, c++, s++)
+ if (c->uc != (unsigned char)*s)
+ return false;
+ return *s == 0;
+}
+
+const HashEntry* Lookup::findEntry( const struct HashTable *table,
+ const UChar *c, unsigned int len )
+{
+#ifndef NDEBUG
+ if (table->type != 2) {
+ fprintf(stderr, "KJS: Unknown hash table version.\n");
+ return 0;
+ }
+#endif
+
+ int h = hash(c, len) % table->hashSize;
+ const HashEntry *e = &table->entries[h];
+
+ // empty bucket ?
+ if (!e->soffset)
+ return 0;
+
+ while(1) {
+ // compare strings
+ if (keysMatch(c, len, &table->sbase[e->soffset]))
+ return e;
+ // try next bucket
+ if(e->next < 0) break;
+
+ e = &table->entries[e->next];
+ }
+
+ return 0;
+}
+
+const HashEntry* Lookup::findEntry( const struct HashTable *table,
+ const Identifier &s )
+{
+ return findEntry( table, s.data(), s.size() );
+}
+
+int Lookup::find(const struct HashTable *table,
+ const UChar *c, unsigned int len)
+{
+ const HashEntry *entry = findEntry( table, c, len );
+ if (entry)
+ return entry->value;
+ return -1;
+}
+
+int Lookup::find(const struct HashTable *table, const Identifier &s)
+{
+ return find(table, s.data(), s.size());
+}
+
+unsigned int Lookup::hash(const UChar *c, unsigned int len)
+{
+ unsigned int val = 0;
+ // ignoring higher byte
+ for (unsigned int i = 0; i < len; i++, c++)
+ val += c->low();
+
+ return val;
+}
+
+unsigned int Lookup::hash(const Identifier &key)
+{
+ return hash(key.data(), key.size());
+}
+
+unsigned int Lookup::hash(const char *s)
+{
+ unsigned int val = 0;
+ while (*s)
+ val += *s++;
+
+ return val;
+}
diff --git a/kjs/lookup.h b/kjs/lookup.h
new file mode 100644
index 000000000..a77b0b633
--- /dev/null
+++ b/kjs/lookup.h
@@ -0,0 +1,401 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 _KJSLOOKUP_H_
+#define _KJSLOOKUP_H_
+
+#include "identifier.h"
+#include "value.h"
+#include "object.h"
+#include "interpreter.h"
+#include <stdio.h>
+
+namespace KJS {
+
+ /**
+ * An entry in a hash table.
+ */
+ struct HashEntry {
+ /**
+ * s is the offset to the string key (e.g. a property name)
+ */
+ unsigned short soffset;
+ /**
+ * value is the result value (usually an enum value)
+ */
+ short int value;
+ /**
+ * attr is a set for flags (e.g. the property flags, see object.h)
+ */
+ unsigned char attr;
+ /**
+ * params is another number. For property hashtables, it is used to
+ * denote the number of argument of the function
+ */
+ unsigned char params;
+ /**
+ * next is the index to the next entry for the same hash value
+ */
+ short next;
+ };
+
+ /**
+ * A hash table
+ * Usually the hashtable is generated by the create_hash_table script, from a .table file.
+ *
+ * The implementation uses an array of entries, "size" is the total size of that array.
+ * The entries between 0 and hashSize-1 are the entry points
+ * for each hash value, and the entries between hashSize and size-1
+ * are the overflow entries for the hash values that need one.
+ * The "next" pointer of the entry links entry points to overflow entries,
+ * and links overflow entries between them.
+ */
+ struct HashTable {
+ /**
+ * type is a version number. Currently always 2
+ */
+ int type;
+ /**
+ * size is the total number of entries in the hashtable, including the null entries,
+ * i.e. the size of the "entries" array.
+ * Used to iterate over all entries in the table
+ */
+ int size;
+ /**
+ * pointer to the array of entries
+ * Mind that some entries in the array are null (0,0,0,0).
+ */
+ const HashEntry *const entries;
+ /**
+ * the maximum value for the hash. Always smaller than size.
+ */
+ int hashSize;
+
+ /**
+ * pointer to the string table.
+ */
+ const char* const sbase;
+ };
+
+ /**
+ * @short Fast keyword lookup.
+ */
+ class KJS_EXPORT Lookup {
+ public:
+ /**
+ * Find an entry in the table, and return its value (i.e. the value field of HashEntry)
+ */
+ static int find(const struct HashTable *table, const Identifier &s);
+ static int find(const struct HashTable *table,
+ const UChar *c, unsigned int len);
+
+ /**
+ * Find an entry in the table, and return the entry
+ * This variant gives access to the other attributes of the entry,
+ * especially the attr field.
+ */
+ static const HashEntry* findEntry(const struct HashTable *table,
+ const Identifier &s);
+ static const HashEntry* findEntry(const struct HashTable *table,
+ const UChar *c, unsigned int len);
+
+ /**
+ * Calculate the hash value for a given key
+ */
+ static unsigned int hash(const Identifier &key);
+ static unsigned int hash(const UChar *c, unsigned int len);
+ static unsigned int hash(const char *s);
+ };
+
+ class ExecState;
+ class UString;
+ /**
+ * @internal
+ * Helper for lookupFunction and lookupValueOrFunction
+ */
+ template <class FuncImp>
+ inline Value lookupOrCreateFunction(ExecState *exec, const Identifier &propertyName,
+ const ObjectImp *thisObj, int token, int params, int attr)
+ {
+ // Look for cached value in dynamic map of properties (in ObjectImp)
+ ValueImp * cachedVal = thisObj->ObjectImp::getDirect(propertyName);
+ /*if (cachedVal)
+ fprintf(stderr, "lookupOrCreateFunction: Function -> looked up in ObjectImp, found type=%d\n", cachedVal->type());*/
+ if (cachedVal)
+ return Value(cachedVal);
+
+ ObjectImp* func = new FuncImp( exec, token, params );
+ Value val( func );
+ func->setFunctionName( propertyName );
+ ObjectImp *thatObj = const_cast<ObjectImp *>(thisObj);
+ thatObj->ObjectImp::put(exec, propertyName, val, attr);
+ return val;
+ }
+
+ /**
+ * 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:
+ * - @c FuncImp the class which implements this object's functions
+ * - @c ThisImp the class of "this". It must implement the
+ * getValueProperty(exec,token) method, for non-function properties.
+ * - @c 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 FuncImp, class ThisImp, class ParentImp>
+ inline Value lookupGet(ExecState *exec, const Identifier &propertyName,
+ const HashTable* table, const ThisImp* thisObj)
+ {
+ const HashEntry* entry = Lookup::findEntry(table, propertyName);
+
+ if (!entry) // not found, forward to parent
+ return thisObj->ParentImp::get(exec, propertyName);
+
+ //fprintf(stderr, "lookupGet: found value=%d attr=%d\n", entry->value, entry->attr);
+ if (entry->attr & Function)
+ return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr);
+ return thisObj->getValueProperty(exec, entry->value);
+ }
+
+ /**
+ * Simplified version of lookupGet in case there are only functions.
+ * Using this instead of lookupGet prevents 'this' from implementing a dummy getValueProperty.
+ */
+ template <class FuncImp, class ParentImp>
+ inline Value lookupGetFunction(ExecState *exec, const Identifier &propertyName,
+ const HashTable* table, const ObjectImp* thisObj)
+ {
+ const HashEntry* entry = Lookup::findEntry(table, propertyName);
+
+ if (!entry) // not found, forward to parent
+ return static_cast<const ParentImp *>(thisObj)->ParentImp::get(exec, propertyName);
+
+ if (entry->attr & Function)
+ return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr);
+
+ fprintf(stderr, "Function bit not set! Shouldn't happen in lookupGetFunction!\n" );
+ return Undefined();
+ }
+
+ /**
+ * Simplified version of lookupGet in case there are no functions, only "values".
+ * Using this instead of lookupGet removes the need for a FuncImp class.
+ */
+ template <class ThisImp, class ParentImp>
+ inline Value lookupGetValue(ExecState *exec, const Identifier &propertyName,
+ const HashTable* table, const ThisImp* thisObj)
+ {
+ const HashEntry* entry = Lookup::findEntry(table, propertyName);
+
+ if (!entry) // not found, forward to parent
+ return thisObj->ParentImp::get(exec, propertyName);
+
+ if (entry->attr & Function)
+ fprintf(stderr, "Function bit set! Shouldn't happen in lookupGetValue! propertyName was %s\n", propertyName.ascii() );
+ return thisObj->getValueProperty(exec, entry->value);
+ }
+
+ /**
+ * This one is for "put".
+ * Lookup hash entry for property to be set, and set the value.
+ */
+ template <class ThisImp, class ParentImp>
+ inline void lookupPut(ExecState *exec, const Identifier &propertyName,
+ const Value& value, int attr,
+ const HashTable* table, ThisImp* thisObj)
+ {
+ const HashEntry* entry = Lookup::findEntry(table, propertyName);
+
+ if (!entry) // not found: forward to parent
+ thisObj->ParentImp::put(exec, propertyName, value, attr);
+ else if (entry->attr & Function) // function: put as override property
+ thisObj->ObjectImp::put(exec, propertyName, value, attr);
+ else if (entry->attr & ReadOnly) // readonly! Can't put!
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName.ascii());
+#else
+ ; // do nothing
+#endif
+ else
+ thisObj->putValueProperty(exec, entry->value, value, attr);
+ }
+
+
+ /**
+ * This template method retrieves or create an object that is unique
+ * (for a given interpreter) The first time this is called (for a given
+ * property name), the Object will be constructed, and set as a property
+ * of the interpreter's global object. Later calls will simply retrieve
+ * that cached object. Note that the object constructor must take 1 argument, exec.
+ */
+ template <class ClassCtor>
+ inline KJS::Object cacheGlobalObject(ExecState *exec, const Identifier &propertyName)
+ {
+ ValueImp *obj = static_cast<KJS::ObjectImp*>(exec->interpreter()->globalObject().imp())->getDirect(propertyName);
+ if (obj)
+ return KJS::Object::dynamicCast(Value(obj));
+ else
+ {
+ KJS::Object newObject(new ClassCtor(exec));
+ exec->interpreter()->globalObject().put(exec, propertyName, newObject, Internal);
+ return newObject;
+ }
+ }
+
+
+ /**
+ * 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.
+ * ParentProto 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. "DOMNodeProtoTable"), then
+ * DEFINE_PROTOTYPE("DOMNode",DOMNodeProto)
+ * IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
+ * IMPLEMENT_PROTOTYPE(DOMNodeProto,DOMNodeProtoFunc)
+ * and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor.
+ * If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto,
+ * then the last line will use IMPLEMENT_PROTOTYPE_WITH_PARENT, with DOMNodeProto as last argument.
+ * PUBLIC_DEFINE_PROTOTYPE and PUBLIC_IMPLEMENT_PROTOTYPE are versions with support for separate compilation
+ */
+
+#define PUBLIC_DEFINE_PROTOTYPE(ClassName,ClassProto) \
+ namespace KJS { \
+ class ClassProto : public KJS::ObjectImp { \
+ friend KJS::Object cacheGlobalObject<ClassProto>(KJS::ExecState *exec, const KJS::Identifier &propertyName); \
+ public: \
+ static KJS::Object self(KJS::ExecState *exec) \
+ { \
+ return cacheGlobalObject<ClassProto>( exec, "[[" ClassName ".prototype]]" ); \
+ } \
+ protected: \
+ ClassProto( KJS::ExecState *exec ) \
+ : KJS::ObjectImp( exec->interpreter()->builtinObjectPrototype() ) {} \
+ \
+ public: \
+ virtual const KJS::ClassInfo *classInfo() const { return &info; } \
+ static const KJS::ClassInfo info; \
+ KJS::Value get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const; \
+ bool hasProperty(KJS::ExecState *exec, const KJS::Identifier &propertyName) const; \
+ }; \
+ }
+
+#define IMPLEMENT_CLASSINFO(ClassName,ClassProto) \
+ namespace KJS {\
+ const KJS::ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \
+ }
+
+#define DEFINE_PROTOTYPE(ClassName,ClassProto) \
+ PUBLIC_DEFINE_PROTOTYPE(ClassName,ClassProto) \
+ IMPLEMENT_CLASSINFO(ClassName,ClassProto)
+
+#define IMPLEMENT_PROTOTYPE(ClassProto,ClassFunc) \
+ KJS::Value KJS::ClassProto::get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \
+ { \
+ /*fprintf( stderr, "%sProto::get(%s) [in macro, no parent]\n", info.className, propertyName.ascii());*/ \
+ return lookupGetFunction<ClassFunc,KJS::ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \
+ } \
+ bool KJS::ClassProto::hasProperty(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \
+ { /*stupid but we need this to have a common macro for the declaration*/ \
+ return KJS::ObjectImp::hasProperty(exec, propertyName); \
+ }
+
+#define PUBLIC_IMPLEMENT_PROTOTYPE(ClassProto,ClassName,ClassFunc) \
+ IMPLEMENT_PROTOTYPE(ClassProto,ClassFunc)\
+ IMPLEMENT_CLASSINFO(ClassName,ClassProto)
+
+#define IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassFunc,ParentProto) \
+ KJS::Value KJS::ClassProto::get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \
+ { \
+ /*fprintf( stderr, "%sProto::get(%s) [in macro]\n", info.className, propertyName.ascii());*/ \
+ KJS::Value val = lookupGetFunction<ClassFunc,KJS::ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \
+ if ( val.type() != UndefinedType ) return val; \
+ /* Not found -> forward request to "parent" prototype */ \
+ return ParentProto::self(exec).get( exec, propertyName ); \
+ } \
+ bool KJS::ClassProto::hasProperty(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \
+ { \
+ if (KJS::ObjectImp::hasProperty(exec, propertyName)) \
+ return true; \
+ return ParentProto::self(exec).hasProperty(exec, propertyName); \
+ }
+
+#define PUBLIC_IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassName,ClassFunc,ParentProto) \
+ IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassFunc,ParentProto) \
+ IMPLEMENT_CLASSINFO(ClassName,ClassProto)
+
+#define IMPLEMENT_PROTOFUNC(ClassFunc) \
+ namespace KJS { \
+ class ClassFunc : public ObjectImp { \
+ public: \
+ ClassFunc(KJS::ExecState *exec, int i, int len) \
+ : ObjectImp( /*proto? */ ), id(i) { \
+ KJS::Value protect(this); \
+ put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); \
+ } \
+ virtual bool implementsCall() const { return true; } \
+ /** You need to implement that one */ \
+ virtual KJS::Value call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args); \
+ private: \
+ int id; \
+ }; \
+ }
+
+ // To be used in all call() implementations, before casting the type of thisObj
+#define KJS_CHECK_THIS( ClassName, theObj ) \
+ if (!theObj.isValid() || !theObj.inherits(&ClassName::info)) { \
+ KJS::UString errMsg = "Attempt at calling a function that expects a "; \
+ errMsg += ClassName::info.className; \
+ errMsg += " on a "; \
+ errMsg += thisObj.className(); \
+ KJS::Object err = KJS::Error::create(exec, KJS::TypeError, errMsg.ascii()); \
+ exec->setException(err); \
+ return err; \
+ }
+
+ /*
+ * List of things to do when porting an objectimp to the 'static hashtable' mechanism:
+ * - write the hashtable source, between @begin and @end
+ * - add a rule to build the .lut.h
+ * - include the .lut.h
+ * - mention the table in the classinfo (add a classinfo if necessary)
+ * - write/update the class enum (for the tokens)
+ * - turn get() into getValueProperty(), put() into putValueProperty(), using a switch and removing funcs
+ * - write get() and/or put() using a template method
+ * - cleanup old stuff (e.g. hasProperty)
+ * - compile, test, commit ;)
+ */
+} // namespace
+
+#endif
diff --git a/kjs/math_object.cpp b/kjs/math_object.cpp
new file mode 100644
index 000000000..6ad8289b0
--- /dev/null
+++ b/kjs/math_object.cpp
@@ -0,0 +1,268 @@
+// -*- 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
+ *
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <time.h>
+
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "math_object.h"
+
+#include "math_object.lut.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+#ifndef signbit
+#define signbit(x) ((x) < 0.0 || IS_NEGATIVE_ZERO(x))
+#endif
+
+using namespace KJS;
+
+// ------------------------------ MathObjectImp --------------------------------
+
+const ClassInfo MathObjectImp::info = { "Math", 0, &mathTable, 0 };
+
+/* Source for math_object.lut.h
+@begin mathTable 31
+ 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 MathObjectImp::Abs DontEnum|Function 1
+ acos MathObjectImp::ACos DontEnum|Function 1
+ asin MathObjectImp::ASin DontEnum|Function 1
+ atan MathObjectImp::ATan DontEnum|Function 1
+ atan2 MathObjectImp::ATan2 DontEnum|Function 2
+ ceil MathObjectImp::Ceil DontEnum|Function 1
+ cos MathObjectImp::Cos DontEnum|Function 1
+ exp MathObjectImp::Exp DontEnum|Function 1
+ floor MathObjectImp::Floor DontEnum|Function 1
+ log MathObjectImp::Log DontEnum|Function 1
+ max MathObjectImp::Max DontEnum|Function 2
+ min MathObjectImp::Min DontEnum|Function 2
+ pow MathObjectImp::Pow DontEnum|Function 2
+ random MathObjectImp::Random DontEnum|Function 0
+ round MathObjectImp::Round DontEnum|Function 1
+ sin MathObjectImp::Sin DontEnum|Function 1
+ sqrt MathObjectImp::Sqrt DontEnum|Function 1
+ tan MathObjectImp::Tan DontEnum|Function 1
+@end
+*/
+
+MathObjectImp::MathObjectImp(ExecState * /*exec*/,
+ ObjectPrototypeImp *objProto)
+ : ObjectImp(objProto)
+{
+ unsigned int seed = time(NULL);
+ ::srand(seed);
+}
+
+// ECMA 15.8
+Value MathObjectImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ return lookupGet<MathFuncImp, MathObjectImp, ObjectImp>( exec, propertyName, &mathTable, this );
+}
+
+Value MathObjectImp::getValueProperty(ExecState *, int token) const
+{
+ double d = -42; // ;)
+ switch (token) {
+ case Euler:
+ d = exp(1.0);
+ break;
+ case Ln2:
+ d = log(2.0);
+ break;
+ case Ln10:
+ d = log(10.0);
+ break;
+ case Log2E:
+ d = 1.0/log(2.0);
+ break;
+ case Log10E:
+ d = 1.0/log(10.0);
+ break;
+ case Pi:
+ d = M_PI;
+ break;
+ case Sqrt1_2:
+ d = sqrt(0.5);
+ break;
+ case Sqrt2:
+ d = sqrt(2.0);
+ break;
+ default:
+ fprintf( stderr, "Internal error in MathObjectImp: unhandled token %d\n", token );
+ break;
+ }
+
+ return Number(d);
+}
+
+// ------------------------------ MathObjectImp --------------------------------
+
+MathFuncImp::MathFuncImp(ExecState *exec, int i, int l)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
+ ), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, l, DontDelete|ReadOnly|DontEnum);
+}
+
+bool MathFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value MathFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ double arg = args[0].toNumber(exec);
+ double arg2 = args[1].toNumber(exec);
+ double result;
+
+ switch (id) {
+ case MathObjectImp::Abs:
+ result = ( arg < 0 || arg == -0) ? (-arg) : arg;
+ break;
+ case MathObjectImp::ACos:
+ result = ::acos(arg);
+ break;
+ case MathObjectImp::ASin:
+ result = ::asin(arg);
+ break;
+ case MathObjectImp::ATan:
+ result = ::atan(arg);
+ break;
+ case MathObjectImp::ATan2:
+ result = ::atan2(arg, arg2);
+ break;
+ case MathObjectImp::Ceil:
+ result = ::ceil(arg);
+ break;
+ case MathObjectImp::Cos:
+ result = ::cos(arg);
+ break;
+ case MathObjectImp::Exp:
+ result = ::exp(arg);
+ break;
+ case MathObjectImp::Floor:
+ result = ::floor(arg);
+ break;
+ case MathObjectImp::Log:
+ result = ::log(arg);
+ break;
+ case MathObjectImp::Max: {
+ unsigned int argsCount = args.size();
+ result = -Inf;
+ for ( unsigned int 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;
+ }
+ break;
+ }
+ case MathObjectImp::Min: {
+ unsigned int argsCount = args.size();
+ result = +Inf;
+ for ( unsigned int 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;
+ }
+ break;
+ }
+ case MathObjectImp::Pow:
+ // ECMA 15.8.2.1.13 (::pow takes care of most of the critera)
+ if (KJS::isNaN(arg2))
+ result = NaN;
+#ifndef APPLE_CHANGES
+ else if (arg2 == 0)
+ result = 1;
+#endif
+ else if (KJS::isNaN(arg) && arg2 != 0)
+ result = NaN;
+#ifndef APPLE_CHANGES
+ else if (::fabs(arg) > 1 && KJS::isPosInf(arg2))
+ result = Inf;
+ else if (::fabs(arg) > 1 && KJS::isNegInf(arg2))
+ result = +0;
+#endif
+ else if (::fabs(arg) == 1 && KJS::isInf(arg2))
+ result = NaN;
+#ifndef APPLE_CHANGES
+ else if (::fabs(arg) < 1 && KJS::isPosInf(arg2))
+ result = +0;
+ else if (::fabs(arg) < 1 && KJS::isNegInf(arg2))
+ result = Inf;
+#endif
+ else
+ result = ::pow(arg, arg2);
+ break;
+ case MathObjectImp::Random:
+ result = ::rand();
+ result = result / RAND_MAX;
+ break;
+ case MathObjectImp::Round:
+ if (signbit(arg) && arg >= -0.5)
+ result = -0.0;
+ else
+ result = ::floor(arg + 0.5);
+ break;
+ case MathObjectImp::Sin:
+ result = ::sin(arg);
+ break;
+ case MathObjectImp::Sqrt:
+ result = ::sqrt(arg);
+ break;
+ case MathObjectImp::Tan:
+ result = ::tan(arg);
+ break;
+
+ default:
+ result = 0.0;
+ assert(0);
+ }
+
+ return Number(result);
+}
diff --git a/kjs/math_object.h b/kjs/math_object.h
new file mode 100644
index 000000000..ff4b1f9d6
--- /dev/null
+++ b/kjs/math_object.h
@@ -0,0 +1,54 @@
+// -*- 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 "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class MathObjectImp : public ObjectImp {
+ public:
+ MathObjectImp(ExecState *exec,
+ ObjectPrototypeImp *objProto);
+ Value get(ExecState *exec, const Identifier &p) const;
+ Value getValueProperty(ExecState *exec, int token) const;
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ enum { Euler, Ln2, Ln10, Log2E, Log10E, Pi, Sqrt1_2, Sqrt2,
+ Abs, ACos, ASin, ATan, ATan2, Ceil, Cos, Pow,
+ Exp, Floor, Log, Max, Min, Random, Round, Sin, Sqrt, Tan };
+ };
+
+ class MathFuncImp : public InternalFunctionImp {
+ public:
+ MathFuncImp(ExecState *exec, int i, int l);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ private:
+ int id;
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/nodes.cpp b/kjs/nodes.cpp
new file mode 100644
index 000000000..c6ee18bf1
--- /dev/null
+++ b/kjs/nodes.cpp
@@ -0,0 +1,3137 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2002, 2003 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "nodes.h"
+
+#include <math.h>
+#include <assert.h>
+#ifdef KJS_DEBUG_MEM
+#include <stdio.h>
+#include <typeinfo>
+#endif
+#ifdef KJS_VERBOSE
+#include <iostream>
+using namespace std;
+#endif
+
+#include "collector.h"
+#include "context.h"
+#include "debugger.h"
+#include "function_object.h"
+#include "internal.h"
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "lexer.h"
+#include "operations.h"
+#include "ustring.h"
+
+using namespace KJS;
+
+#define KJS_BREAKPOINT \
+ if (!hitStatement(exec)) \
+ return Completion(Normal);
+
+#define KJS_ABORTPOINT \
+ if (exec->dynamicInterpreter()->imp()->debugger() && \
+ exec->dynamicInterpreter()->imp()->debugger()->imp()->aborted()) \
+ return Completion(Normal);
+
+#define KJS_CHECKEXCEPTION \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return Completion(Throw, exec->exception()); \
+ } \
+ if (Collector::outOfMemory()) \
+ return Completion(Throw, Error::create(exec,GeneralError,"Out of memory"));
+
+#define KJS_CHECKEXCEPTIONVALUE \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return exec->exception(); \
+ } \
+ if (Collector::outOfMemory()) \
+ return Undefined(); // will be picked up by KJS_CHECKEXCEPTION
+
+#define KJS_CHECKEXCEPTIONREFERENCE \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return Reference::makeValueReference(Undefined()); \
+ } \
+ if (Collector::outOfMemory()) \
+ return Reference::makeValueReference(Undefined()); // will be picked up by KJS_CHECKEXCEPTION
+
+#define KJS_CHECKEXCEPTIONLIST \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return List(); \
+ } \
+ if (Collector::outOfMemory()) \
+ return List(); // will be picked up by KJS_CHECKEXCEPTION
+
+#ifdef KJS_DEBUG_MEM
+std::list<Node *> * Node::s_nodes = 0L;
+#endif
+
+// ----------------------------- Node -----------------------------------------
+
+Node::Node()
+{
+ line = Lexer::curr()->lineNo();
+ refcount = 0;
+#ifdef KJS_DEBUG_MEM
+ if (!s_nodes)
+ s_nodes = new std::list<Node *>;
+ s_nodes->push_back(this);
+#endif
+}
+
+Node::~Node()
+{
+#ifdef KJS_DEBUG_MEM
+ s_nodes->remove( this );
+#endif
+}
+
+Reference Node::evaluateReference(ExecState *exec) const
+{
+ Value v = evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+ return Reference::makeValueReference(v);
+}
+
+// fallback for those nodes without a evaluate() reimplementation
+// TODO: reimplemint in each sub class, make Node::evaluate() pure virtual
+Value Node::evaluate(ExecState *exec) const
+{
+ // fprintf(stderr, "%s::evaluate()\n", typeid(*this).name());
+ return evaluateReference(exec).getValue(exec);
+}
+
+bool Node::toBoolean(ExecState *exec) const
+{
+// fprintf(stderr, "Node(%s)::toBoolean()\n", typeid(*this).name());
+ return evaluate(exec).toBoolean(exec);
+}
+
+double Node::toNumber(ExecState *exec) const
+{
+// fprintf(stderr, "Node(%s)::toNumber()\n", typeid(*this).name());
+ return evaluate(exec).toNumber(exec);
+}
+
+UString Node::toString(ExecState *exec) const
+{
+ return evaluate(exec).toString(exec);
+}
+
+#ifdef KJS_DEBUG_MEM
+void Node::finalCheck()
+{
+ if (!s_nodes) {
+ fprintf(stderr, "Node::finalCheck(): list 0\n");
+ return;
+ }
+ fprintf( stderr, "Node::finalCheck(): list count : %d\n", (int)s_nodes->size() );
+ std::list<Node *>::iterator it = s_nodes->begin();
+ for ( uint i = 0; it != s_nodes->end() ; ++it, ++i )
+ fprintf( stderr, "[%d] Still having node %p (%s) (refcount %d)\n", i, (void*)*it, typeid( **it ).name(), (*it)->refcount );
+ delete s_nodes;
+ s_nodes = 0L;
+}
+#endif
+
+Value Node::throwError(ExecState *exec, ErrorType e, const char *msg) const
+{
+ Object err = Error::create(exec, e, msg, lineNo(), sourceId());
+ exec->setException(err);
+ return err;
+}
+
+Value Node::throwError(ExecState *exec, ErrorType e, const char *msg,
+ const Value &v, const Node *expr) const
+{
+ char *vStr = strdup(v.toString(exec).ascii());
+ char *exprStr = strdup(expr->toCode().ascii());
+
+ int length = strlen(msg) - 4 /* two %s */ + strlen(vStr) + strlen(exprStr) + 1 /* null terminator */;
+ char *str = new char[length];
+ sprintf(str, msg, vStr, exprStr);
+ free(vStr);
+ free(exprStr);
+
+ Value result = throwError(exec, e, str);
+ delete [] str;
+
+ return result;
+}
+
+Value Node::throwError(ExecState *exec, ErrorType e, const char *msg, Identifier label) const
+{
+ const char *l = label.ascii();
+ int length = strlen(msg) - 2 /* %s */ + strlen(l) + 1 /* null terminator */;
+ char *message = new char[length];
+ sprintf(message, msg, l);
+
+ Value result = throwError(exec, e, message);
+ delete [] message;
+
+ return result;
+}
+
+
+void Node::setExceptionDetailsIfNeeded(ExecState *exec) const
+{
+ if (exec->hadException()) {
+ Object exception = exec->exception().toObject(exec);
+ if (!exception.hasProperty(exec, "line") /* &&
+ !exception.hasProperty(exec, "sourceURL")*/ ) {
+ exception.put(exec, "line", Number(line));
+// exception.put(exec, "sourceURL", String(sourceURL));
+ }
+ }
+}
+
+// ----------------------------- StatementNode --------------------------------
+StatementNode::StatementNode() : l0(-1), l1(-1), sourceCode(0), breakPoint(false)
+{
+}
+
+StatementNode::~StatementNode()
+{
+ if (sourceCode)
+ sourceCode->deref();
+}
+
+void StatementNode::setLoc(int line0, int line1, SourceCode *src)
+{
+ // ### require these to be passed to the constructor
+ l0 = line0;
+ l1 = line1;
+ if (sourceCode != src) {
+ if (sourceCode)
+ sourceCode->deref();
+ sourceCode = src;
+ sourceCode->ref();
+ }
+}
+
+// return true if the debugger wants us to stop at this point
+bool StatementNode::hitStatement(ExecState *exec)
+{
+ assert(sourceCode);
+ assert(exec->context().imp()->sourceId == sourceCode->sid);
+ exec->context().imp()->setLines(l0,l1);
+ Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
+ if (dbg)
+ return dbg->atStatement(exec);
+ else
+ return true; // continue
+}
+
+// return true if the debugger wants us to stop at this point
+bool StatementNode::abortStatement(ExecState *exec)
+{
+ Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
+ if (dbg)
+ return dbg->imp()->aborted();
+ else
+ return false;
+}
+
+void StatementNode::processFuncDecl(ExecState *)
+{
+}
+
+// ----------------------------- NullNode -------------------------------------
+
+Value NullNode::evaluate(ExecState *) const
+{
+ return Null();
+}
+
+bool NullNode::toBoolean(ExecState *) const
+{
+ return false;
+}
+
+double NullNode::toNumber(ExecState *) const
+{
+ return 0.0;
+}
+
+UString NullNode::toString(ExecState *) const
+{
+ return "null";
+}
+
+// ----------------------------- BooleanNode ----------------------------------
+
+Value BooleanNode::evaluate(ExecState *) const
+{
+ return Boolean(val);
+}
+
+bool BooleanNode::toBoolean(ExecState *) const
+{
+ return val;
+}
+
+double BooleanNode::toNumber(ExecState *) const
+{
+ return val ? 1.0 : 0.0;
+}
+
+UString BooleanNode::toString(ExecState *) const
+{
+ return val ? "true" : "false";
+}
+
+// ----------------------------- NumberNode -----------------------------------
+
+Value NumberNode::evaluate(ExecState *) const
+{
+ return Number(val);
+}
+
+bool NumberNode::toBoolean(ExecState *) const
+{
+ return !((val == 0) /* || (iVal() == N0) */ || isNaN(val));
+}
+
+double NumberNode::toNumber(ExecState *) const
+{
+ return val;
+}
+
+UString NumberNode::toString(ExecState *) const
+{
+ return UString::from(val);
+}
+
+// ----------------------------- StringNode -----------------------------------
+
+Value StringNode::evaluate(ExecState *) const
+{
+ return String(val);
+}
+
+bool StringNode::toBoolean(ExecState *) const
+{
+ return !val.isEmpty();
+}
+
+double StringNode::toNumber(ExecState *) const
+{
+ return val.toDouble();
+}
+
+UString StringNode::toString(ExecState *) const
+{
+ return val;
+}
+
+// ----------------------------- RegExpNode -----------------------------------
+
+Value RegExpNode::evaluate(ExecState *exec) const
+{
+ List list;
+ String p(pattern);
+ String f(flags);
+ list.append(p);
+ list.append(f);
+
+ Object reg = exec->lexicalInterpreter()->imp()->builtinRegExp();
+ return reg.construct(exec,list);
+}
+
+bool RegExpNode::toBoolean(ExecState *) const
+{
+ return true;
+}
+
+// ----------------------------- ThisNode -------------------------------------
+
+// ECMA 11.1.1
+Value ThisNode::evaluate(ExecState *exec) const
+{
+ return exec->context().imp()->thisValue();
+}
+
+// ----------------------------- ResolveNode ----------------------------------
+
+// ECMA 11.1.2 & 10.1.4
+Value ResolveNode::evaluate(ExecState *exec) const
+{
+ return evaluateReference(exec).getValue(exec);
+}
+
+Reference ResolveNode::evaluateReference(ExecState *exec) const
+{
+ ScopeChain chain = exec->context().imp()->scopeChain();
+
+ while (!chain.isEmpty()) {
+ ObjectImp *o = chain.top();
+
+ //cerr << "Resolve: looking at '" << ident.ascii() << "'"
+ // << " in " << (void*)o << " " << o->classInfo()->className << endl;
+ if (o->hasProperty(exec,ident)) {
+ //cerr << "Resolve: FOUND '" << ident.ascii() << "'"
+ // << " in " << (void*)o << " " << o->classInfo()->className << endl;
+ return Reference(o, ident);
+ }
+
+ chain.pop();
+ }
+
+ // identifier not found
+#ifdef KJS_VERBOSE
+ cerr << "Resolve::evaluateReference: didn't find '" << ident.ustring().ascii() << "'" << endl;
+#endif
+ return Reference(Null(), ident);
+}
+
+// ----------------------------- GroupNode ------------------------------------
+
+void GroupNode::ref()
+{
+ Node::ref();
+ if ( group )
+ group->ref();
+}
+
+bool GroupNode::deref()
+{
+ if ( group && group->deref() )
+ delete group;
+ return Node::deref();
+}
+
+// ECMA 11.1.6
+Value GroupNode::evaluate(ExecState *exec) const
+{
+ return group->evaluate(exec);
+}
+
+Reference GroupNode::evaluateReference(ExecState *exec) const
+{
+ return group->evaluateReference(exec);
+}
+
+// ----------------------------- ElementNode ----------------------------------
+
+void ElementNode::ref()
+{
+ for (ElementNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->node)
+ n->node->ref();
+ }
+}
+
+bool ElementNode::deref()
+{
+ ElementNode *next;
+ for (ElementNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->node && n->node->deref())
+ delete n->node;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+// ECMA 11.1.4
+Value ElementNode::evaluate(ExecState *exec) const
+{
+ Object array = exec->lexicalInterpreter()->builtinArray().construct(exec, List::empty());
+ int length = 0;
+ for (const ElementNode *n = this; n; n = n->list) {
+ Value val = n->node->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ length += n->elision;
+ array.put(exec, length++, val);
+ }
+ return array;
+}
+
+// ----------------------------- ArrayNode ------------------------------------
+
+void ArrayNode::ref()
+{
+ Node::ref();
+ if ( element )
+ element->ref();
+}
+
+bool ArrayNode::deref()
+{
+ if ( element && element->deref() )
+ delete element;
+ return Node::deref();
+}
+
+// ECMA 11.1.4
+Value ArrayNode::evaluate(ExecState *exec) const
+{
+ Object array;
+ int length;
+
+ if (element) {
+ array = Object(static_cast<ObjectImp*>(element->evaluate(exec).imp()));
+ KJS_CHECKEXCEPTIONVALUE
+ length = opt ? array.get(exec,lengthPropertyName).toInt32(exec) : 0;
+ } else {
+ Value newArr = exec->lexicalInterpreter()->builtinArray().construct(exec,List::empty());
+ array = Object(static_cast<ObjectImp*>(newArr.imp()));
+ length = 0;
+ }
+
+ if (opt)
+ array.put(exec,lengthPropertyName, Number(elision + length), DontEnum | DontDelete);
+
+ return array;
+}
+
+// ----------------------------- ObjectLiteralNode ----------------------------
+
+void ObjectLiteralNode::ref()
+{
+ Node::ref();
+ if ( list )
+ list->ref();
+}
+
+bool ObjectLiteralNode::deref()
+{
+ if ( list && list->deref() )
+ delete list;
+ return Node::deref();
+}
+
+// ECMA 11.1.5
+Value ObjectLiteralNode::evaluate(ExecState *exec) const
+{
+ if (list)
+ return list->evaluate(exec);
+
+ return exec->lexicalInterpreter()->builtinObject().construct(exec,List::empty());
+}
+
+// ----------------------------- PropertyValueNode ----------------------------
+
+void PropertyValueNode::ref()
+{
+ for (PropertyValueNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->name)
+ n->name->ref();
+ if (n->assign)
+ n->assign->ref();
+ }
+}
+
+bool PropertyValueNode::deref()
+{
+ PropertyValueNode *next;
+ for (PropertyValueNode *n = this; n; n = next) {
+ next = n->list;
+ if ( n->name && n->name->deref() )
+ delete n->name;
+ if ( n->assign && n->assign->deref() )
+ delete n->assign;
+ if (n != this && n->Node::deref() )
+ delete n;
+ }
+ return Node::deref();
+}
+
+// ECMA 11.1.5
+Value PropertyValueNode::evaluate(ExecState *exec) const
+{
+ Object obj = exec->lexicalInterpreter()->builtinObject().construct(exec, List::empty());
+
+ for (const PropertyValueNode *p = this; p; p = p->list) {
+ Value n = p->name->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = p->assign->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ obj.put(exec, Identifier(n.toString(exec)), v);
+ }
+
+ return obj;
+}
+
+// ----------------------------- PropertyNode ---------------------------------
+
+// ECMA 11.1.5
+Value PropertyNode::evaluate(ExecState * /*exec*/) const
+{
+ Value s;
+
+ if (str.isNull()) {
+ s = String(UString::from(numeric));
+ } else {
+ s = String(str.ustring());
+ }
+
+ return s;
+}
+
+// ----------------------------- AccessorNode1 --------------------------------
+
+void AccessorNode1::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+}
+
+bool AccessorNode1::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ return Node::deref();
+}
+
+// ECMA 11.2.1a
+Reference AccessorNode1::evaluateReference(ExecState *exec) const
+{
+ Value v1 = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+ Value v2 = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+#ifndef NDEBUG
+ // catch errors before being caught in toObject(). better error message.
+ if (v1.isA(UndefinedType) || v1.isA(NullType)) {
+ UString s = "Attempted to access property on %s object "
+ "(result of expression %s)";
+ (void)throwError(exec, TypeError, s.cstring().c_str(), v1, this);
+ return Reference::makeValueReference(Undefined());
+ }
+#endif
+ Object o = v1.toObject(exec);
+ unsigned i;
+ if (v2.toUInt32(i))
+ return Reference(o, i);
+ UString s = v2.toString(exec);
+ return Reference(o, Identifier(s));
+}
+
+// ----------------------------- AccessorNode2 --------------------------------
+
+void AccessorNode2::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool AccessorNode2::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.2.1b
+Reference AccessorNode2::evaluateReference(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+ assert(v.isValid());
+#ifndef NDEBUG
+ // catch errors before being caught in toObject(). better error message.
+ if (v.isA(UndefinedType) || v.isA(NullType)) {
+ UString s = "Attempted to access '" + ident.ustring() +
+ "' property on %s object (result of expression %s)";
+ (void)throwError(exec, TypeError, s.cstring().c_str(), v, this);
+ return Reference::makeValueReference(Undefined());
+ }
+#endif
+ Object o = v.toObject(exec);
+ return Reference(o, ident);
+}
+
+// ----------------------------- ArgumentListNode -----------------------------
+
+void ArgumentListNode::ref()
+{
+ for (ArgumentListNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->expr)
+ n->expr->ref();
+ }
+}
+
+bool ArgumentListNode::deref()
+{
+ ArgumentListNode *next;
+ for (ArgumentListNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->expr && n->expr->deref())
+ delete n->expr;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+Value ArgumentListNode::evaluate(ExecState * /*exec*/) const
+{
+ assert(0);
+ return Value(); // dummy, see evaluateList()
+}
+
+// ECMA 11.2.4
+List ArgumentListNode::evaluateList(ExecState *exec) const
+{
+ List l;
+
+ for (const ArgumentListNode *n = this; n; n = n->list) {
+ Value v = n->expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONLIST
+ l.append(v);
+ }
+
+ return l;
+}
+
+// ----------------------------- ArgumentsNode --------------------------------
+
+void ArgumentsNode::ref()
+{
+ Node::ref();
+ if ( list )
+ list->ref();
+}
+
+bool ArgumentsNode::deref()
+{
+ if ( list && list->deref() )
+ delete list;
+ return Node::deref();
+}
+
+Value ArgumentsNode::evaluate(ExecState * /*exec*/) const
+{
+ assert(0);
+ return Value(); // dummy, see evaluateList()
+}
+
+// ECMA 11.2.4
+List ArgumentsNode::evaluateList(ExecState *exec) const
+{
+ if (!list)
+ return List();
+
+ return list->evaluateList(exec);
+}
+
+// ----------------------------- NewExprNode ----------------------------------
+
+// ECMA 11.2.2
+
+void NewExprNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+ if ( args )
+ args->ref();
+}
+
+bool NewExprNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ if ( args && args->deref() )
+ delete args;
+ return Node::deref();
+}
+
+Value NewExprNode::evaluate(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ List argList;
+ if (args) {
+ argList = args->evaluateList(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ }
+
+ if (v.type() != ObjectType) {
+ return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with new.", v, expr);
+ }
+
+ Object constr = Object(static_cast<ObjectImp*>(v.imp()));
+ if (!constr.implementsConstruct()) {
+ return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, expr);
+ }
+
+ Value res = constr.construct(exec,argList);
+
+ return res;
+}
+
+// ----------------------------- FunctionCallNode -----------------------------
+
+void FunctionCallNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+ if ( args )
+ args->ref();
+}
+
+bool FunctionCallNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ if ( args && args->deref() )
+ delete args;
+ return Node::deref();
+}
+
+// ECMA 11.2.3
+Value FunctionCallNode::evaluate(ExecState *exec) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ List argList = args->evaluateList(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v = ref.getValue(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ if (v.type() != ObjectType) {
+ return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be called.", v, expr);
+ }
+
+ Object func = Object(static_cast<ObjectImp*>(v.imp()));
+
+ if (!func.implementsCall()) {
+ return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, expr);
+ }
+
+ Value thisVal;
+ if (ref.isMutable())
+ thisVal = ref.getBase(exec);
+ else
+ thisVal = Null();
+
+ if (thisVal.type() == ObjectType &&
+ Object::dynamicCast(thisVal).inherits(&ActivationImp::info))
+ thisVal = Null();
+
+ if (thisVal.type() != ObjectType) {
+ // 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 interal 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.
+ // thisVal = Null();
+ thisVal = exec->dynamicInterpreter()->globalObject();
+ }
+
+ Object thisObj = Object::dynamicCast(thisVal);
+ Value result = func.call(exec,thisObj, argList);
+
+ return result;
+}
+
+// ----------------------------- PostfixNode ----------------------------------
+
+void PostfixNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool PostfixNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.3
+Value PostfixNode::evaluate(ExecState *exec) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = ref.getValue(exec);
+ double n = v.toNumber(exec);
+
+ double newValue = (oper == OpPlusPlus) ? n + 1 : n - 1;
+
+ ref.putValue(exec, Number(newValue));
+
+ return Number(n);
+}
+
+// ----------------------------- DeleteNode -----------------------------------
+
+void DeleteNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool DeleteNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.1
+Value DeleteNode::evaluate(ExecState *exec) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ return Boolean(ref.deleteValue(exec));
+}
+
+// ----------------------------- VoidNode -------------------------------------
+
+void VoidNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool VoidNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.2
+Value VoidNode::evaluate(ExecState *exec) const
+{
+ Value dummy1 = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return Undefined();
+}
+
+// ----------------------------- TypeOfNode -----------------------------------
+
+void TypeOfNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool TypeOfNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.3
+Value TypeOfNode::evaluate(ExecState *exec) const
+{
+ const char *s = 0L;
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ if (ref.isMutable()) {
+ Value b = ref.getBase(exec);
+ if (b.type() == NullType)
+ return String("undefined");
+ }
+ Value v = ref.getValue(exec);
+ switch (v.type())
+ {
+ case UndefinedType:
+ s = "undefined";
+ break;
+ case NullType:
+ s = "object";
+ break;
+ case BooleanType:
+ s = "boolean";
+ break;
+ case NumberType:
+ s = "number";
+ break;
+ case StringType:
+ s = "string";
+ break;
+ default:
+ if (v.type() == ObjectType && static_cast<ObjectImp*>(v.imp())->implementsCall())
+ s = "function";
+ else
+ s = "object";
+ break;
+ }
+
+ return String(s);
+}
+
+// ----------------------------- PrefixNode -----------------------------------
+
+void PrefixNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool PrefixNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.4 and 11.4.5
+Value PrefixNode::evaluate(ExecState *exec) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = ref.getValue(exec);
+ double n = v.toNumber(exec);
+
+ double newValue = (oper == OpPlusPlus) ? n + 1 : n - 1;
+ Value n2 = Number(newValue);
+
+ ref.putValue(exec,n2);
+
+ return n2;
+}
+
+// ----------------------------- UnaryPlusNode --------------------------------
+
+void UnaryPlusNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool UnaryPlusNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.6
+double UnaryPlusNode::toNumber(ExecState *exec) const
+{
+ return expr->toNumber(exec);
+}
+
+// could go
+Value UnaryPlusNode::evaluate(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return Number(v.toNumber(exec)); /* TODO: optimize */
+}
+
+// ----------------------------- NegateNode -----------------------------------
+
+void NegateNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool NegateNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.7
+double NegateNode::toNumber(ExecState *exec) const
+{
+ return -expr->toNumber(exec);
+}
+
+Value NegateNode::evaluate(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ double d = -v.toNumber(exec);
+
+ return Number(d);
+}
+
+// ----------------------------- BitwiseNotNode -------------------------------
+
+void BitwiseNotNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool BitwiseNotNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.8
+Value BitwiseNotNode::evaluate(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ int i32 = v.toInt32(exec);
+
+ return Number(~i32);
+}
+
+// ----------------------------- LogicalNotNode -------------------------------
+
+void LogicalNotNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool LogicalNotNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.4.9
+bool LogicalNotNode::toBoolean(ExecState *exec) const
+{
+ return !expr->toBoolean(exec);
+}
+
+// could remove this
+Value LogicalNotNode::evaluate(ExecState *exec) const
+{
+ bool b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return Boolean(!b);
+}
+
+// ----------------------------- MultNode -------------------------------------
+
+void MultNode::ref()
+{
+ Node::ref();
+ if ( term1 )
+ term1->ref();
+ if ( term2 )
+ term2->ref();
+}
+
+bool MultNode::deref()
+{
+ if ( term1 && term1->deref() )
+ delete term1;
+ if ( term2 && term2->deref() )
+ delete term2;
+ return Node::deref();
+}
+
+// ECMA 11.5
+Value MultNode::evaluate(ExecState *exec) const
+{
+ Value v1 = term1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v2 = term2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return mult(exec,v1, v2, oper);
+}
+
+// ----------------------------- AddNode --------------------------------------
+
+// factory for an appropriate addition or substraction node
+Node* AddNode::create(Node *t1, Node *t2, char op)
+{
+ // ### many more combinations to check for
+ // fold constants
+ if ((t1->type() == NumberType || t1->type() == BooleanType) &&
+ (t2->type() == NumberType || t2->type() == BooleanType)) {
+ double d = t2->toNumber(0);
+ Node* n = new NumberNode(t1->toNumber(0) + (op == '+' ? d : -d));
+ delete t1;
+ delete t2;
+ return n;
+ }
+
+ if (op == '+' && t2->type() == StringType)
+ return new AppendStringNode(t1, t2->toString(0));
+
+ // fall back to generic node
+ return new AddNode(t1, t2, op);
+}
+
+void AddNode::ref()
+{
+ Node::ref();
+ if ( term1 )
+ term1->ref();
+ if ( term2 )
+ term2->ref();
+}
+
+bool AddNode::deref()
+{
+ if ( term1 && term1->deref() )
+ delete term1;
+ if ( term2 && term2->deref() )
+ delete term2;
+ return Node::deref();
+}
+
+// ECMA 11.6
+Value AddNode::evaluate(ExecState *exec) const
+{
+ Value v1 = term1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v2 = term2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return add(exec,v1, v2, oper);
+}
+
+// ------------------------ AddNumberNode ------------------------------------
+
+void AppendStringNode::ref()
+{
+ Node::ref();
+ term->ref();
+}
+
+bool AppendStringNode::deref()
+{
+ if (term->deref())
+ delete term;
+ return Node::deref();
+}
+
+// ECMA 11.6 (special case of string appending)
+Value AppendStringNode::evaluate(ExecState *exec) const
+{
+ UString s = term->toString(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return String(s + str);
+}
+
+// ----------------------------- ShiftNode ------------------------------------
+
+void ShiftNode::ref()
+{
+ Node::ref();
+ if ( term1 )
+ term1->ref();
+ if ( term2 )
+ term2->ref();
+}
+
+bool ShiftNode::deref()
+{
+ if ( term1 && term1->deref() )
+ delete term1;
+ if ( term2 && term2->deref() )
+ delete term2;
+ return Node::deref();
+}
+
+// ECMA 11.7
+Value ShiftNode::evaluate(ExecState *exec) const
+{
+ Value v1 = term1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v2 = term2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ unsigned int i2 = v2.toUInt32(exec);
+ i2 &= 0x1f;
+
+ switch (oper) {
+ case OpLShift:
+ return Number(v1.toInt32(exec) << i2);
+ case OpRShift:
+ return Number(v1.toInt32(exec) >> i2);
+ case OpURShift:
+ return Number(v1.toUInt32(exec) >> i2);
+ default:
+ assert(!"ShiftNode: unhandled switch case");
+ return Undefined();
+ }
+}
+
+// ----------------------------- RelationalNode -------------------------------
+
+void RelationalNode::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+}
+
+bool RelationalNode::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ return Node::deref();
+}
+
+// ECMA 11.8
+Value RelationalNode::evaluate(ExecState *exec) const
+{
+ Value v1 = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v2 = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ bool b;
+ if (oper == OpLess || oper == OpGreaterEq) {
+ int r = relation(exec, v1, v2);
+ if (r < 0)
+ b = false;
+ else
+ b = (oper == OpLess) ? (r == 1) : (r == 0);
+ } else if (oper == OpGreater || oper == OpLessEq) {
+ int r = relation(exec, v2, v1);
+ if (r < 0)
+ b = false;
+ else
+ b = (oper == OpGreater) ? (r == 1) : (r == 0);
+ } else if (oper == OpIn) {
+ // Is all of this OK for host objects?
+ if (v2.type() != ObjectType)
+ return throwError(exec, TypeError,
+ "Value %s (result of expression %s) is not an object. Cannot be used with IN expression.", v2, expr2);
+ Object o2(static_cast<ObjectImp*>(v2.imp()));
+ b = o2.hasProperty(exec,Identifier(v1.toString(exec)));
+ } else {
+ if (v2.type() != ObjectType)
+ return throwError(exec, TypeError,
+ "Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, expr2);
+
+ Object o2(static_cast<ObjectImp*>(v2.imp()));
+ if (!o2.implementsHasInstance()) {
+ // According to the spec, only some types of objects "imlement" the [[HasInstance]] property.
+ // But we are supposed to throw an exception where the object does not "have" the [[HasInstance]]
+ // property. It seems that all object have the property, but not all implement it, so in this
+ // case we return false (consistent with mozilla)
+ return Boolean(false);
+ // return throwError(exec, TypeError,
+ // "Object does not implement the [[HasInstance]] method." );
+ }
+ return o2.hasInstance(exec, v1);
+ }
+
+ return Boolean(b);
+}
+
+// ----------------------------- EqualNode ------------------------------------
+
+void EqualNode::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+}
+
+bool EqualNode::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ return Node::deref();
+}
+
+// ECMA 11.9
+Value EqualNode::evaluate(ExecState *exec) const
+{
+ Value v1 = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v2 = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ bool result;
+ if (oper == OpEqEq || oper == OpNotEq) {
+ // == and !=
+ bool eq = equal(exec,v1, v2);
+ result = oper == OpEqEq ? eq : !eq;
+ } else {
+ // === and !==
+ bool eq = strictEqual(exec,v1, v2);
+ result = oper == OpStrEq ? eq : !eq;
+ }
+ return Boolean(result);
+}
+
+// ----------------------------- BitOperNode ----------------------------------
+
+void BitOperNode::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+}
+
+bool BitOperNode::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ return Node::deref();
+}
+
+// ECMA 11.10
+Value BitOperNode::evaluate(ExecState *exec) const
+{
+ Value v1 = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v2 = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ int i1 = v1.toInt32(exec);
+ int i2 = v2.toInt32(exec);
+ int result;
+ if (oper == OpBitAnd)
+ result = i1 & i2;
+ else if (oper == OpBitXOr)
+ result = i1 ^ i2;
+ else
+ result = i1 | i2;
+
+ return Number(result);
+}
+
+// ----------------------------- BinaryLogicalNode ----------------------------
+
+void BinaryLogicalNode::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+}
+
+bool BinaryLogicalNode::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ return Node::deref();
+}
+
+// ECMA 11.11
+Value BinaryLogicalNode::evaluate(ExecState *exec) const
+{
+ Value v1 = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ bool b1 = v1.toBoolean(exec);
+ if ((!b1 && oper == OpAnd) || (b1 && oper == OpOr))
+ return v1;
+
+ Value v2 = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v2;
+}
+
+// ----------------------------- ConditionalNode ------------------------------
+
+void ConditionalNode::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+ if ( logical )
+ logical->ref();
+}
+
+bool ConditionalNode::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ if ( logical && logical->deref() )
+ delete logical;
+ return Node::deref();
+}
+
+// ECMA 11.12
+Value ConditionalNode::evaluate(ExecState *exec) const
+{
+ bool b = logical->toBoolean(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v = b ? expr1->evaluate(exec) : expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v;
+}
+
+// ----------------------------- AssignNode -----------------------------------
+
+void AssignNode::ref()
+{
+ Node::ref();
+ if ( left )
+ left->ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool AssignNode::deref()
+{
+ if ( left && left->deref() )
+ delete left;
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 11.13
+Value AssignNode::evaluate(ExecState *exec) const
+{
+ Reference l = left->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v;
+ if (oper == OpEqual) {
+ v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ } else {
+ Value v1 = l.getValue(exec);
+ Value v2 = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ int i1;
+ int i2;
+ unsigned int ui;
+ switch (oper) {
+ case OpMultEq:
+ v = mult(exec, v1, v2, '*');
+ break;
+ case OpDivEq:
+ v = mult(exec, v1, v2, '/');
+ break;
+ case OpPlusEq:
+ v = add(exec, v1, v2, '+');
+ break;
+ case OpMinusEq:
+ v = add(exec, v1, v2, '-');
+ break;
+ case OpLShift:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 << i2);
+ break;
+ case OpRShift:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 >> i2);
+ break;
+ case OpURShift:
+ ui = v1.toUInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(ui >> i2);
+ break;
+ case OpAndEq:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 & i2);
+ break;
+ case OpXOrEq:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 ^ i2);
+ break;
+ case OpOrEq:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 | i2);
+ break;
+ case OpModEq: {
+ double d1 = v1.toNumber(exec);
+ double d2 = v2.toNumber(exec);
+ v = Number(fmod(d1,d2));
+ }
+ break;
+ default:
+ v = Undefined();
+ }
+ };
+ l.putValue(exec,v);
+
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v;
+}
+
+// ----------------------------- CommaNode ------------------------------------
+
+void CommaNode::ref()
+{
+ Node::ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+}
+
+bool CommaNode::deref()
+{
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ return Node::deref();
+}
+
+// ECMA 11.14
+Value CommaNode::evaluate(ExecState *exec) const
+{
+ (void) expr1->evaluate(exec); // ignore return value
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v;
+}
+
+// ----------------------------- StatListNode ---------------------------------
+
+StatListNode::StatListNode(StatementNode *s)
+ : statement(s), list(this)
+{
+ setLoc(s->firstLine(), s->lastLine(), s->code());
+}
+
+StatListNode::StatListNode(StatListNode *l, StatementNode *s)
+ : statement(s), list(l->list)
+{
+ l->list = this;
+ setLoc(l->firstLine(),s->lastLine(),l->code());
+}
+
+void StatListNode::ref()
+{
+ for (StatListNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->statement)
+ n->statement->ref();
+ }
+}
+
+bool StatListNode::deref()
+{
+ StatListNode *next;
+ for (StatListNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->statement && n->statement->deref())
+ delete n->statement;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return StatementNode::deref();
+}
+
+// ECMA 12.1
+Completion StatListNode::execute(ExecState *exec)
+{
+ Completion c = statement->execute(exec);
+ KJS_ABORTPOINT
+ if (exec->hadException()) {
+ Value ex = exec->exception();
+ exec->clearException();
+ return Completion(Throw, ex);
+ }
+
+ if (c.complType() != Normal)
+ return c;
+
+ Value v = c.value();
+
+ for (StatListNode *n = list; n; n = n->list) {
+ Completion c2 = n->statement->execute(exec);
+ KJS_ABORTPOINT
+ if (c2.complType() != Normal)
+ return c2;
+
+ if (exec->hadException()) {
+ Value ex = exec->exception();
+ exec->clearException();
+ return Completion(Throw, ex);
+ }
+
+ if (c2.isValueCompletion())
+ v = c2.value();
+ c = c2;
+ }
+
+ return Completion(c.complType(), v, c.target());
+}
+
+void StatListNode::processVarDecls(ExecState *exec)
+{
+ for (StatListNode *n = this; n; n = n->list)
+ n->statement->processVarDecls(exec);
+}
+
+// ----------------------------- AssignExprNode -------------------------------
+
+void AssignExprNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool AssignExprNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return Node::deref();
+}
+
+// ECMA 12.2
+Value AssignExprNode::evaluate(ExecState *exec) const
+{
+ return expr->evaluate(exec);
+}
+
+// ----------------------------- VarDeclNode ----------------------------------
+
+VarDeclNode::VarDeclNode(const Identifier &id, AssignExprNode *in, Type t)
+ : varType(t), ident(id), init(in)
+{
+}
+
+void VarDeclNode::ref()
+{
+ Node::ref();
+ if ( init )
+ init->ref();
+}
+
+bool VarDeclNode::deref()
+{
+ if ( init && init->deref() )
+ delete init;
+ return Node::deref();
+}
+
+// ECMA 12.2
+Value VarDeclNode::evaluate(ExecState *exec) const
+{
+ Object variable = Object::dynamicCast(exec->context().imp()->variableObject());
+
+ Value val;
+ if (init) {
+ val = init->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ } else {
+ // ### check attributes? reuse check done in processVarDecls()?
+ if (variable.imp()->getDirect(ident)) // already declared ?
+ return Value();
+ val = Undefined();
+ }
+
+#ifdef KJS_VERBOSE
+ printInfo(exec,(UString("new variable ")+ident.ustring()).cstring().c_str(),val);
+#endif
+ // We use Internal to bypass all checks in derived objects, e.g. so that
+ // "var location" creates a dynamic property instead of activating window.location.
+ int flags = Internal;
+ if (exec->context().imp()->codeType() != EvalCode)
+ flags |= DontDelete;
+ if (varType == VarDeclNode::Constant)
+ flags |= ReadOnly;
+ variable.put(exec, ident, val, flags);
+
+ // the spec wants us to return the name of the identifier here
+ // but we'll save the construction and copying as the return
+ // value isn't used by the caller
+ return Value();
+}
+
+void VarDeclNode::processVarDecls(ExecState *exec)
+{
+ Object variable = exec->context().variableObject();
+ // ### use getDirect()? Check attributes?
+ // ### avoid duplication with actions performed in evaluate()?
+ if ( !variable.hasProperty( exec, ident ) ) { // already declared ?
+ int flags = None;
+ if (exec->_context->codeType() != EvalCode)
+ flags |= DontDelete;
+ if (varType == VarDeclNode::Constant)
+ flags |= ReadOnly;
+ // TODO: check for forbidden redeclaration of consts
+ variable.put(exec, ident, Undefined(), flags);
+ }
+}
+
+// ----------------------------- VarDeclListNode ------------------------------
+
+void VarDeclListNode::ref()
+{
+ for (VarDeclListNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->var)
+ n->var->ref();
+ }
+}
+
+bool VarDeclListNode::deref()
+{
+ VarDeclListNode *next;
+ for (VarDeclListNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->var && n->var->deref())
+ delete n->var;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+
+// ECMA 12.2
+Value VarDeclListNode::evaluate(ExecState *exec) const
+{
+ for (const VarDeclListNode *n = this; n; n = n->list) {
+ (void)n->var->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ }
+ return Undefined();
+}
+
+void VarDeclListNode::processVarDecls(ExecState *exec)
+{
+ for (VarDeclListNode *n = this; n; n = n->list)
+ n->var->processVarDecls(exec);
+}
+
+// ----------------------------- VarStatementNode -----------------------------
+
+void VarStatementNode::ref()
+{
+ StatementNode::ref();
+ if ( list )
+ list->ref();
+}
+
+bool VarStatementNode::deref()
+{
+ if ( list && list->deref() )
+ delete list;
+ return StatementNode::deref();
+}
+
+// ECMA 12.2
+Completion VarStatementNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ (void) list->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ return Completion(Normal);
+}
+
+void VarStatementNode::processVarDecls(ExecState *exec)
+{
+ list->processVarDecls(exec);
+}
+
+// ----------------------------- BlockNode ------------------------------------
+
+BlockNode::BlockNode(SourceElementsNode *s)
+{
+ if (s) {
+ source = s->elements;
+ s->elements = 0;
+ setLoc(s->firstLine(), s->lastLine(), s->code());
+ } else {
+ source = 0;
+ }
+}
+
+void BlockNode::ref()
+{
+ StatementNode::ref();
+ if ( source )
+ source->ref();
+}
+
+bool BlockNode::deref()
+{
+ if ( source && source->deref() )
+ delete source;
+ return StatementNode::deref();
+}
+
+// ECMA 12.1
+Completion BlockNode::execute(ExecState *exec)
+{
+ if (!source)
+ return Completion(Normal);
+
+ source->processFuncDecl(exec);
+
+ return source->execute(exec);
+}
+
+void BlockNode::processVarDecls(ExecState *exec)
+{
+ if (source)
+ source->processVarDecls(exec);
+}
+
+// ----------------------------- EmptyStatementNode ---------------------------
+
+// ECMA 12.3
+Completion EmptyStatementNode::execute(ExecState * /*exec*/)
+{
+ return Completion(Normal);
+}
+
+// ----------------------------- ExprStatementNode ----------------------------
+
+void ExprStatementNode::ref()
+{
+ StatementNode::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool ExprStatementNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.4
+Completion ExprStatementNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ return Completion(Normal, v);
+}
+
+// ----------------------------- IfNode ---------------------------------------
+
+void IfNode::ref()
+{
+ StatementNode::ref();
+ if ( statement1 )
+ statement1->ref();
+ if ( statement2 )
+ statement2->ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool IfNode::deref()
+{
+ if ( statement1 && statement1->deref() )
+ delete statement1;
+ if ( statement2 && statement2->deref() )
+ delete statement2;
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.5
+Completion IfNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ assert(expr);
+ bool b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+
+ // if ... then
+ if (b)
+ return statement1->execute(exec);
+
+ // no else
+ if (!statement2)
+ return Completion(Normal);
+
+ // else
+ return statement2->execute(exec);
+}
+
+void IfNode::processVarDecls(ExecState *exec)
+{
+ statement1->processVarDecls(exec);
+
+ if (statement2)
+ statement2->processVarDecls(exec);
+}
+
+// ----------------------------- DoWhileNode ----------------------------------
+
+void DoWhileNode::ref()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool DoWhileNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.6.1
+Completion DoWhileNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Completion c;
+ Value value;
+ bool b;
+
+ do {
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ if (!((c.complType() == Continue) && ls.contains(c.target()))) {
+ if ((c.complType() == Break) && ls.contains(c.target()))
+ return Completion(Normal, value);
+ if (c.complType() != Normal)
+ return c;
+ }
+ b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+ } while (b);
+
+ return Completion(Normal, value);
+}
+
+void DoWhileNode::processVarDecls(ExecState *exec)
+{
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- WhileNode ------------------------------------
+
+void WhileNode::ref()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool WhileNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.6.2
+Completion WhileNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Completion c;
+ Value value;
+
+ while (1) {
+ bool b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ if (!b)
+ return Completion(Normal, value);
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ if (c.isValueCompletion())
+ value = c.value();
+
+ if ((c.complType() == Continue) && ls.contains(c.target()))
+ continue;
+ if ((c.complType() == Break) && ls.contains(c.target()))
+ return Completion(Normal, value);
+ if (c.complType() != Normal)
+ return c;
+ }
+}
+
+void WhileNode::processVarDecls(ExecState *exec)
+{
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- ForNode --------------------------------------
+
+void ForNode::ref()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+ if ( expr1 )
+ expr1->ref();
+ if ( expr2 )
+ expr2->ref();
+ if ( expr3 )
+ expr3->ref();
+}
+
+bool ForNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ if ( expr1 && expr1->deref() )
+ delete expr1;
+ if ( expr2 && expr2->deref() )
+ delete expr2;
+ if ( expr3 && expr3->deref() )
+ delete expr3;
+ return StatementNode::deref();
+}
+
+// ECMA 12.6.3
+Completion ForNode::execute(ExecState *exec)
+{
+ Value v, cval;
+
+ if (expr1) {
+ v = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ }
+ for (;;) {
+ if (expr2) {
+ bool b = expr2->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+ if (!b)
+ return Completion(Normal, cval);
+ }
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ Completion c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ if (c.isValueCompletion())
+ cval = c.value();
+ if (!((c.complType() == Continue) && ls.contains(c.target()))) {
+ if ((c.complType() == Break) && ls.contains(c.target()))
+ return Completion(Normal, cval);
+ if (c.complType() != Normal)
+ return c;
+ }
+ if (expr3) {
+ v = expr3->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ }
+ }
+}
+
+void ForNode::processVarDecls(ExecState *exec)
+{
+ if (expr1)
+ expr1->processVarDecls(exec);
+
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- ForInNode ------------------------------------
+
+ForInNode::ForInNode(Node *l, Node *e, StatementNode *s)
+ : init(0L), lexpr(l), expr(e), varDecl(0L), statement(s)
+{
+}
+
+ForInNode::ForInNode(const Identifier &i, AssignExprNode *in, Node *e, StatementNode *s)
+ : ident(i), init(in), expr(e), statement(s)
+{
+ // for( var foo = bar in baz )
+ varDecl = new VarDeclNode(ident, init, VarDeclNode::Variable);
+ lexpr = new ResolveNode(ident);
+}
+
+void ForInNode::ref()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+ if ( expr )
+ expr->ref();
+ if ( lexpr )
+ lexpr->ref();
+ if ( init )
+ init->ref();
+ if ( varDecl )
+ varDecl->ref();
+}
+
+bool ForInNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ if ( expr && expr->deref() )
+ delete expr;
+ if ( lexpr && lexpr->deref() )
+ delete lexpr;
+ if ( init && init->deref() )
+ delete init;
+ if ( varDecl && varDecl->deref() )
+ delete varDecl;
+ return StatementNode::deref();
+}
+
+// ECMA 12.6.4
+Completion ForInNode::execute(ExecState *exec)
+{
+ Value retval;
+ Completion c;
+
+ if ( varDecl ) {
+ (void)varDecl->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ }
+
+ Value v = expr->evaluate(exec);
+ // for Null and Undefined, we want to make sure not to go through
+ // the loop at all, because their object wrappers will have a
+ // property list but will throw an exception if you attempt to
+ // access any property.
+ if (v.isA(NullType) || v.isA(UndefinedType))
+ return Completion(Normal, retval);
+
+ Object o = v.toObject(exec);
+ KJS_CHECKEXCEPTION
+ ReferenceList propList = o.propList(exec);
+
+ ReferenceListIterator propIt = propList.begin();
+
+ while (propIt != propList.end()) {
+ Identifier name = propIt->getPropertyName(exec);
+ if (!o.hasProperty(exec,name)) {
+ propIt++;
+ continue;
+ }
+
+ Reference ref = lexpr->evaluateReference(exec);
+ KJS_CHECKEXCEPTION
+ ref.putValue(exec, String(name.ustring()));
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ if (c.isValueCompletion())
+ retval = c.value();
+
+ if (!((c.complType() == Continue) && ls.contains(c.target()))) {
+ if ((c.complType() == Break) && ls.contains(c.target()))
+ break;
+ if (c.complType() != Normal) {
+ return c;
+ }
+ }
+
+ propIt++;
+ }
+
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ return Completion(Normal, retval);
+}
+
+void ForInNode::processVarDecls(ExecState *exec)
+{
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- ContinueNode ---------------------------------
+
+// ECMA 12.7
+Completion ContinueNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value dummy;
+
+ if (ident.isEmpty() && !exec->context().imp()->seenLabels()->inIteration())
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "continue used outside of iteration statement"));
+ else if (!ident.isEmpty() && !exec->context().imp()->seenLabels()->contains(ident))
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "Label %s not found in containing block. Can't continue.", ident));
+ else
+ return Completion(Continue, dummy, ident);
+}
+
+// ----------------------------- BreakNode ------------------------------------
+
+// ECMA 12.8
+Completion BreakNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value dummy;
+
+ if (ident.isEmpty() && !exec->context().imp()->seenLabels()->inIteration() &&
+ !exec->context().imp()->seenLabels()->inSwitch())
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "break used outside of iteration or switch statement"));
+ else if (!ident.isEmpty() && !exec->context().imp()->seenLabels()->contains(ident))
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "Label %s not found in containing block. Can't break.", ident));
+ else
+ return Completion(Break, dummy, ident);
+}
+
+// ----------------------------- ReturnNode -----------------------------------
+
+void ReturnNode::ref()
+{
+ StatementNode::ref();
+ if ( value )
+ value->ref();
+}
+
+bool ReturnNode::deref()
+{
+ if ( value && value->deref() )
+ delete value;
+ return StatementNode::deref();
+}
+
+// ECMA 12.9
+Completion ReturnNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ CodeType codeType = exec->context().imp()->codeType();
+ if (codeType != FunctionCode) {
+ return Completion(Throw, throwError(exec, SyntaxError, "Invalid return statement."));
+ }
+
+ if (!value)
+ return Completion(ReturnValue, Undefined());
+
+ Value v = value->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ return Completion(ReturnValue, v);
+}
+
+// ----------------------------- WithNode -------------------------------------
+
+void WithNode::ref()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool WithNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.10
+Completion WithNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ Object o = v.toObject(exec);
+ KJS_CHECKEXCEPTION
+ exec->context().imp()->pushScope(o);
+ Completion res = statement->execute(exec);
+ exec->context().imp()->popScope();
+
+ return res;
+}
+
+void WithNode::processVarDecls(ExecState *exec)
+{
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- CaseClauseNode -------------------------------
+
+void CaseClauseNode::ref()
+{
+ Node::ref();
+ if ( expr )
+ expr->ref();
+ if ( list )
+ list->ref();
+}
+
+bool CaseClauseNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ if ( list && list->deref() )
+ delete list;
+ return Node::deref();
+}
+
+// ECMA 12.11
+Value CaseClauseNode::evaluate(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v;
+}
+
+// ECMA 12.11
+Completion CaseClauseNode::evalStatements(ExecState *exec) const
+{
+ if (list)
+ return list->execute(exec);
+ else
+ return Completion(Normal, Undefined());
+}
+
+void CaseClauseNode::processVarDecls(ExecState *exec)
+{
+ if (list)
+ list->processVarDecls(exec);
+}
+
+// ----------------------------- ClauseListNode -------------------------------
+
+void ClauseListNode::ref()
+{
+ for (ClauseListNode *n = this; n; n = n->nx) {
+ n->Node::ref();
+ if (n->cl)
+ n->cl->ref();
+ }
+}
+
+bool ClauseListNode::deref()
+{
+ ClauseListNode *next;
+ for (ClauseListNode *n = this; n; n = next) {
+ next = n->nx;
+ if (n->cl && n->cl->deref())
+ delete n->cl;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+Value ClauseListNode::evaluate(ExecState * /*exec*/) const
+{
+ /* should never be called */
+ assert(false);
+ return Value();
+}
+
+// ECMA 12.11
+void ClauseListNode::processVarDecls(ExecState *exec)
+{
+ for (ClauseListNode *n = this; n; n = n->nx)
+ if (n->cl)
+ n->cl->processVarDecls(exec);
+}
+
+// ----------------------------- CaseBlockNode --------------------------------
+
+CaseBlockNode::CaseBlockNode(ClauseListNode *l1, CaseClauseNode *d,
+ ClauseListNode *l2)
+{
+ def = d;
+ if (l1) {
+ list1 = l1->nx;
+ l1->nx = 0;
+ } else {
+ list1 = 0;
+ }
+ if (l2) {
+ list2 = l2->nx;
+ l2->nx = 0;
+ } else {
+ list2 = 0;
+ }
+}
+
+void CaseBlockNode::ref()
+{
+ Node::ref();
+ if ( def )
+ def->ref();
+ if ( list1 )
+ list1->ref();
+ if ( list2 )
+ list2->ref();
+}
+
+bool CaseBlockNode::deref()
+{
+ if ( def && def->deref() )
+ delete def;
+ if ( list1 && list1->deref() )
+ delete list1;
+ if ( list2 && list2->deref() )
+ delete list2;
+ return Node::deref();
+}
+
+Value CaseBlockNode::evaluate(ExecState * /*exec*/) const
+{
+ /* should never be called */
+ assert(false);
+ return Value();
+}
+
+// ECMA 12.11
+Completion CaseBlockNode::evalBlock(ExecState *exec, const Value& input) const
+{
+ Value v;
+ Completion res;
+ ClauseListNode *a = list1, *b = list2;
+ CaseClauseNode *clause;
+
+ while (a) {
+ clause = a->clause();
+ a = a->next();
+ v = clause->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ if (strictEqual(exec, input, v)) {
+ res = clause->evalStatements(exec);
+ if (res.complType() != Normal)
+ return res;
+ while (a) {
+ res = a->clause()->evalStatements(exec);
+ if (res.complType() != Normal)
+ return res;
+ a = a->next();
+ }
+ break;
+ }
+ }
+
+ while (b) {
+ clause = b->clause();
+ b = b->next();
+ v = clause->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ if (strictEqual(exec, input, v)) {
+ res = clause->evalStatements(exec);
+ if (res.complType() != Normal)
+ return res;
+ goto step18;
+ }
+ }
+
+ // default clause
+ if (def) {
+ res = def->evalStatements(exec);
+ if (res.complType() != Normal)
+ return res;
+ }
+ b = list2;
+ step18:
+ while (b) {
+ clause = b->clause();
+ res = clause->evalStatements(exec);
+ if (res.complType() != Normal)
+ return res;
+ b = b->next();
+ }
+
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ return Completion(Normal);
+}
+
+void CaseBlockNode::processVarDecls(ExecState *exec)
+{
+ if (list1)
+ list1->processVarDecls(exec);
+ if (def)
+ def->processVarDecls(exec);
+ if (list2)
+ list2->processVarDecls(exec);
+}
+
+// ----------------------------- SwitchNode -----------------------------------
+
+void SwitchNode::ref()
+{
+ StatementNode::ref();
+ if ( expr )
+ expr->ref();
+ if ( block )
+ block->ref();
+}
+
+bool SwitchNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ if ( block && block->deref() )
+ delete block;
+ return StatementNode::deref();
+}
+
+// ECMA 12.11
+Completion SwitchNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ exec->context().imp()->seenLabels()->pushSwitch();
+ Completion res = block->evalBlock(exec,v);
+ exec->context().imp()->seenLabels()->popSwitch();
+
+ if ((res.complType() == Break) && ls.contains(res.target()))
+ return Completion(Normal, res.value());
+ else
+ return res;
+}
+
+void SwitchNode::processVarDecls(ExecState *exec)
+{
+ block->processVarDecls(exec);
+}
+
+// ----------------------------- LabelNode ------------------------------------
+
+void LabelNode::ref()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+}
+
+bool LabelNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ return StatementNode::deref();
+}
+
+// ECMA 12.12
+Completion LabelNode::execute(ExecState *exec)
+{
+ Completion e;
+
+ if (!exec->context().imp()->seenLabels()->push(label)) {
+ return Completion( Throw,
+ throwError(exec, SyntaxError, "Duplicated label %s found.", label));
+ };
+ e = statement->execute(exec);
+ exec->context().imp()->seenLabels()->pop();
+
+ if ((e.complType() == Break) && (e.target() == label))
+ return Completion(Normal, e.value());
+ else
+ return e;
+}
+
+void LabelNode::processVarDecls(ExecState *exec)
+{
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- ThrowNode ------------------------------------
+
+void ThrowNode::ref()
+{
+ StatementNode::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool ThrowNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.13
+Completion ThrowNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ Debugger *dbg = exec->interpreter()->imp()->debugger();
+ if (dbg)
+ dbg->exception(exec,v,exec->context().imp()->inTryCatch());
+
+ return Completion(Throw, v);
+}
+
+// ----------------------------- CatchNode ------------------------------------
+
+void CatchNode::ref()
+{
+ StatementNode::ref();
+ if ( block )
+ block->ref();
+}
+
+bool CatchNode::deref()
+{
+ if ( block && block->deref() )
+ delete block;
+ return StatementNode::deref();
+}
+
+Completion CatchNode::execute(ExecState * /*exec*/)
+{
+ // should never be reached. execute(exec, arg) is used instead
+ assert(0L);
+ return Completion();
+}
+
+// ECMA 12.14
+Completion CatchNode::execute(ExecState *exec, const Value &arg)
+{
+ /* TODO: correct ? Not part of the spec */
+
+ exec->clearException();
+
+ Object obj(new ObjectImp());
+ obj.put(exec, ident, arg, DontDelete);
+ exec->context().imp()->pushScope(obj);
+ Completion c = block->execute(exec);
+ exec->context().imp()->popScope();
+
+ return c;
+}
+
+void CatchNode::processVarDecls(ExecState *exec)
+{
+ block->processVarDecls(exec);
+}
+
+// ----------------------------- FinallyNode ----------------------------------
+
+void FinallyNode::ref()
+{
+ StatementNode::ref();
+ if ( block )
+ block->ref();
+}
+
+bool FinallyNode::deref()
+{
+ if ( block && block->deref() )
+ delete block;
+ return StatementNode::deref();
+}
+
+// ECMA 12.14
+Completion FinallyNode::execute(ExecState *exec)
+{
+ return block->execute(exec);
+}
+
+void FinallyNode::processVarDecls(ExecState *exec)
+{
+ block->processVarDecls(exec);
+}
+
+// ----------------------------- TryNode --------------------------------------
+
+void TryNode::ref()
+{
+ StatementNode::ref();
+ if ( block )
+ block->ref();
+ if ( _final )
+ _final->ref();
+ if ( _catch )
+ _catch->ref();
+}
+
+bool TryNode::deref()
+{
+ if ( block && block->deref() )
+ delete block;
+ if ( _final && _final->deref() )
+ delete _final;
+ if ( _catch && _catch->deref() )
+ delete _catch;
+ return StatementNode::deref();
+}
+
+// ECMA 12.14
+Completion TryNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Completion c, c2;
+
+ if (_catch)
+ exec->context().imp()->pushTryCatch();
+ c = block->execute(exec);
+ if (_catch)
+ exec->context().imp()->popTryCatch();
+
+ if (!_final) {
+ if (c.complType() != Throw)
+ return c;
+ return _catch->execute(exec,c.value());
+ }
+
+ if (!_catch) {
+ Value exception = exec->_exception;
+ exec->_exception = Value();
+
+ c2 = _final->execute(exec);
+
+ if (!exec->hadException() && c2.complType() != Throw)
+ exec->_exception = exception;
+
+ return (c2.complType() == Normal) ? c : c2;
+ }
+
+ if (c.complType() == Throw)
+ c = _catch->execute(exec,c.value());
+
+ c2 = _final->execute(exec);
+ return (c2.complType() == Normal) ? c : c2;
+}
+
+void TryNode::processVarDecls(ExecState *exec)
+{
+ block->processVarDecls(exec);
+ if (_final)
+ _final->processVarDecls(exec);
+ if (_catch)
+ _catch->processVarDecls(exec);
+}
+
+// ----------------------------- ParameterNode --------------------------------
+
+void ParameterNode::ref()
+{
+ for (ParameterNode *n = this; n; n = n->next)
+ n->Node::ref();
+}
+
+bool ParameterNode::deref()
+{
+ ParameterNode *next;
+ for (ParameterNode *n = this; n; n = next) {
+ next = n->next;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+// ECMA 13
+Value ParameterNode::evaluate(ExecState * /*exec*/) const
+{
+ return Undefined();
+}
+
+// ----------------------------- FunctionBodyNode -----------------------------
+
+
+FunctionBodyNode::FunctionBodyNode(SourceElementsNode *s)
+ : BlockNode(s)
+{
+ //fprintf(stderr,"FunctionBodyNode::FunctionBodyNode %p\n",this);
+}
+
+void FunctionBodyNode::processFuncDecl(ExecState *exec)
+{
+ if (source)
+ source->processFuncDecl(exec);
+}
+
+// ----------------------------- FuncDeclNode ---------------------------------
+
+void FuncDeclNode::ref()
+{
+ StatementNode::ref();
+ if ( param )
+ param->ref();
+ if ( body )
+ body->ref();
+}
+
+bool FuncDeclNode::deref()
+{
+ if ( param && param->deref() )
+ delete param;
+ if ( body && body->deref() )
+ delete body;
+ return StatementNode::deref();
+}
+
+// ECMA 13
+void FuncDeclNode::processFuncDecl(ExecState *exec)
+{
+ ContextImp *ctx = exec->context().imp();
+ // TODO: let this be an object with [[Class]] property "Function"
+ FunctionImp *fimp = new DeclaredFunctionImp(exec, ident, body, exec->context().imp()->scopeChain());
+ Object func(fimp); // protect from GC
+
+ // Value proto = exec->lexicalInterpreter()->builtinObject().construct(exec,List::empty());
+ List empty;
+ Object proto = exec->lexicalInterpreter()->builtinObject().construct(exec,empty);
+ proto.put(exec, constructorPropertyName, func, ReadOnly|DontDelete|DontEnum);
+ func.put(exec, prototypePropertyName, proto, Internal|DontDelete);
+
+ int plen = 0;
+ for(const ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
+ fimp->addParameter(p->ident());
+
+ func.put(exec, lengthPropertyName, Number(plen), ReadOnly|DontDelete|DontEnum);
+
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"KJS: new function %s in %p\n", ident.ustring().cstring().c_str(), ctx->variableObject().imp());
+#endif
+ if (exec->_context->codeType() == EvalCode) {
+ // ECMA 10.2.2
+ ctx->variableObject().put(exec, ident, func, Internal);
+ } else {
+ ctx->variableObject().put(exec, ident, func, DontDelete | Internal);
+ }
+
+ if (body) {
+ // hack the scope so that the function gets put as a property of func, and it's scope
+ // contains the func as well as our current scope
+ Object oldVar = ctx->variableObject();
+ ctx->setVariableObject(func);
+ ctx->pushScope(func);
+ body->processFuncDecl(exec);
+ ctx->popScope();
+ ctx->setVariableObject(oldVar);
+ }
+}
+
+// ----------------------------- FuncExprNode ---------------------------------
+
+void FuncExprNode::ref()
+{
+ Node::ref();
+ if ( param )
+ param->ref();
+ if ( body )
+ body->ref();
+}
+
+bool FuncExprNode::deref()
+{
+ if ( param && param->deref() )
+ delete param;
+ if ( body && body->deref() )
+ delete body;
+ return Node::deref();
+}
+
+
+// ECMA 13
+Value FuncExprNode::evaluate(ExecState *exec) const
+{
+ ContextImp *context = exec->context().imp();
+ bool named = !ident.isNull();
+ Object functionScopeObject;
+
+ 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 = Object(new ObjectImp());
+ context->pushScope(functionScopeObject);
+ }
+
+ FunctionImp *fimp = new DeclaredFunctionImp(exec, Identifier::null(), body, exec->context().imp()->scopeChain());
+ Value ret(fimp);
+ List empty;
+ Value proto = exec->lexicalInterpreter()->builtinObject().construct(exec,empty);
+ fimp->put(exec, prototypePropertyName, proto, Internal|DontDelete);
+
+ for(const ParameterNode *p = param; p != 0L; p = p->nextParam())
+ fimp->addParameter(p->ident());
+
+ if (named) {
+ functionScopeObject.put(exec, ident, Value(fimp), ReadOnly|DontDelete);
+ context->popScope();
+ }
+
+ return ret;
+}
+
+// ----------------------------- SourceElementsNode ---------------------------
+
+SourceElementsNode::SourceElementsNode(StatementNode *s1)
+{
+ element = s1;
+ elements = this;
+ setLoc(s1->firstLine(), s1->lastLine(), s1->code());
+}
+
+SourceElementsNode::SourceElementsNode(SourceElementsNode *s1, StatementNode *s2)
+{
+ elements = s1->elements;
+ s1->elements = this;
+ element = s2;
+ setLoc(s1->firstLine(), s2->lastLine(), s1->code());
+}
+
+void SourceElementsNode::ref()
+{
+ for (SourceElementsNode *n = this; n; n = n->elements) {
+ n->Node::ref();
+ if (n->element)
+ n->element->ref();
+ }
+}
+
+bool SourceElementsNode::deref()
+{
+ SourceElementsNode *next;
+ for (SourceElementsNode *n = this; n; n = next) {
+ next = n->elements;
+ if (n->element && n->element->deref())
+ delete n->element;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return StatementNode::deref();
+}
+
+// ECMA 14
+Completion SourceElementsNode::execute(ExecState *exec)
+{
+ KJS_CHECKEXCEPTION
+
+ Completion c1 = element->execute(exec);
+ KJS_CHECKEXCEPTION;
+ if (c1.complType() != Normal)
+ return c1;
+
+ for (SourceElementsNode *n = elements; n; n = n->elements) {
+ Completion c2 = n->element->execute(exec);
+ if (c2.complType() != Normal)
+ return c2;
+ // The spec says to return c2 here, but it seems that mozilla returns c1 if
+ // c2 doesn't have a value
+ if (c2.value().isValid())
+ c1 = c2;
+ }
+
+ return c1;
+}
+
+// ECMA 14
+void SourceElementsNode::processFuncDecl(ExecState *exec)
+{
+ for (SourceElementsNode *n = this; n; n = n->elements)
+ n->element->processFuncDecl(exec);
+}
+
+void SourceElementsNode::processVarDecls(ExecState *exec)
+{
+ for (SourceElementsNode *n = this; n; n = n->elements)
+ n->element->processVarDecls(exec);
+}
diff --git a/kjs/nodes.h b/kjs/nodes.h
new file mode 100644
index 000000000..4f7e10d36
--- /dev/null
+++ b/kjs/nodes.h
@@ -0,0 +1,1082 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000, 2003 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * 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 _NODES_H_
+#define _NODES_H_
+
+#include "internal.h"
+//#include "debugger.h"
+#ifndef NDEBUG
+#include <list>
+#include <assert.h>
+#endif
+
+namespace KJS {
+
+ class RegExp;
+ class SourceElementsNode;
+ class ObjectLiteralNode;
+ class PropertyNode;
+ class SourceStream;
+ class PropertyValueNode;
+ class PropertyNode;
+
+ enum Operator { OpEqual,
+ OpEqEq,
+ OpNotEq,
+ OpStrEq,
+ OpStrNEq,
+ OpPlusEq,
+ OpMinusEq,
+ OpMultEq,
+ OpDivEq,
+ OpPlusPlus,
+ OpMinusMinus,
+ OpLess,
+ OpLessEq,
+ OpGreater,
+ OpGreaterEq,
+ OpAndEq,
+ OpXOrEq,
+ OpOrEq,
+ OpModEq,
+ OpAnd,
+ OpOr,
+ OpBitAnd,
+ OpBitXOr,
+ OpBitOr,
+ OpLShift,
+ OpRShift,
+ OpURShift,
+ OpIn,
+ OpInstanceOf
+ };
+
+ class Node {
+ public:
+ Node();
+ virtual ~Node();
+
+ // reusing Value Type here, declare new enum if required
+ virtual Type type() const { return UnspecifiedType; }
+
+ /**
+ * Evaluate this node and return the result, possibly a reference.
+ */
+ virtual Reference evaluateReference(ExecState *exec) const;
+ /**
+ * Returns the value represented by this node. Always dereferenced.
+ */
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual UString toString(ExecState *exec) const;
+
+ UString toCode() const;
+ virtual void streamTo(SourceStream &s) const = 0;
+ virtual void processVarDecls(ExecState* /*exec*/) {}
+ int lineNo() const { return line; }
+
+ public:
+ // reference counting mechanism
+ virtual void ref() { refcount++; }
+#ifdef KJS_DEBUG_MEM
+ virtual bool deref() { assert( refcount > 0 ); return (!--refcount); }
+#else
+ virtual bool deref() { return (!--refcount); }
+#endif
+
+
+#ifdef KJS_DEBUG_MEM
+ static void finalCheck();
+#endif
+ protected:
+ Value throwError(ExecState *exec, ErrorType e, const char *msg) const;
+ Value throwError(ExecState *exec, ErrorType e, const char *msg,
+ const Value &v, const Node *expr) const;
+ Value throwError(ExecState *exec, ErrorType e, const char *msg, Identifier label) const;
+ void setExceptionDetailsIfNeeded(ExecState *exec) const;
+ int line;
+ unsigned int refcount;
+ virtual int sourceId() const { return -1; }
+ private:
+#ifdef KJS_DEBUG_MEM
+ // List of all nodes, for debugging purposes. Don't remove!
+ static std::list<Node *> *s_nodes;
+#endif
+ // disallow assignment
+ Node& operator=(const Node&);
+ Node(const Node &other);
+ };
+
+ class StatementNode : public Node {
+ public:
+ StatementNode();
+ virtual ~StatementNode();
+ void setLoc(int line0, int line1, SourceCode *src);
+ int firstLine() const { return l0; }
+ int lastLine() const { return l1; }
+ int sourceId() const { return sourceCode->sid; }
+ SourceCode *code() const { return sourceCode; }
+ bool hitStatement(ExecState *exec);
+ bool abortStatement(ExecState *exec);
+ virtual Completion execute(ExecState *exec) = 0;
+ void pushLabel(const Identifier &id) { ls.push(id); }
+ virtual void processFuncDecl(ExecState *exec);
+ protected:
+ LabelStack ls;
+ private:
+ Reference evaluateReference(ExecState* /*exec*/) const { return Reference(0,Identifier::null()); }
+ int l0, l1;
+ SourceCode *sourceCode;
+ bool breakPoint;
+ };
+
+ class NullNode : public Node {
+ public:
+ NullNode() {}
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual UString toString(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ };
+
+ class BooleanNode : public Node {
+ public:
+ BooleanNode(bool v) : val(v) {}
+ virtual Type type() const { return BooleanType; }
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual UString toString(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ bool val;
+ };
+
+ class NumberNode : public Node {
+ public:
+ NumberNode(double v) : val(v) { }
+ virtual Type type() const { return NumberType; }
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual UString toString(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ double val;
+ };
+
+ class StringNode : public Node {
+ public:
+ StringNode(const UString *v) : val(*v) { }
+ virtual Type type() const { return StringType; }
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual UString toString(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ UString val;
+ };
+
+ class RegExpNode : public Node {
+ public:
+ RegExpNode(const UString &p, const UString &f)
+ : pattern(p), flags(f) { }
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ UString pattern, flags;
+ };
+
+ class ThisNode : public Node {
+ public:
+ ThisNode() {}
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ };
+
+ class ResolveNode : public Node {
+ public:
+ ResolveNode(const Identifier &s) : ident(s) { }
+ Reference evaluateReference(ExecState *exec) const;
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ };
+
+ class GroupNode : public Node {
+ public:
+ GroupNode(Node *g) : group(g) { }
+ virtual void ref();
+ virtual bool deref();
+ Reference evaluateReference(ExecState *exec) const;
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *group;
+ };
+
+ class ElementNode : public Node {
+ public:
+ // list is circular during construction. cracked in ArrayNode ctor
+ ElementNode(int e, Node *n) : list(this), elision(e), node(n) { }
+ ElementNode(ElementNode *l, int e, Node *n)
+ : list(l->list), elision(e), node(n) { l->list = this; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class ArrayNode;
+ ElementNode *list;
+ int elision;
+ Node *node;
+ };
+
+ class ArrayNode : public Node {
+ public:
+ ArrayNode(int e) : element(0L), elision(e), opt(true) { }
+ ArrayNode(ElementNode *ele)
+ : element(ele->list), elision(0), opt(false) { ele->list = 0; }
+ ArrayNode(int eli, ElementNode *ele)
+ : element(ele->list), elision(eli), opt(true) { ele->list = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ ElementNode *element;
+ int elision;
+ bool opt;
+ };
+
+ class PropertyValueNode : public Node {
+ public:
+ // list is circular during construction, cut in ObjectLiteralNode ctor
+ PropertyValueNode(PropertyNode *n, Node *a)
+ : name(n), assign(a), list(this) { }
+ PropertyValueNode(PropertyNode *n, Node *a, PropertyValueNode *l)
+ : name(n), assign(a), list(l->list) { l->list = this; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class ObjectLiteralNode;
+ PropertyNode *name;
+ Node *assign;
+ PropertyValueNode *list;
+ };
+
+ class PropertyNode : public Node {
+ public:
+ PropertyNode(double d) : numeric(d) { }
+ PropertyNode(const Identifier &s) : str(s) { }
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ double numeric;
+ Identifier str;
+ };
+
+ class ObjectLiteralNode : public Node {
+ public:
+ // empty literal
+ ObjectLiteralNode() : list(0) { }
+ // l points to last list element, get and detach pointer to first one
+ ObjectLiteralNode(PropertyValueNode *l) : list(l->list) { l->list = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ PropertyValueNode *list;
+ };
+
+ class AccessorNode1 : public Node {
+ public:
+ AccessorNode1(Node *e1, Node *e2) : expr1(e1), expr2(e2) {}
+ virtual void ref();
+ virtual bool deref();
+ Reference evaluateReference(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1;
+ Node *expr2;
+ };
+
+ class AccessorNode2 : public Node {
+ public:
+ AccessorNode2(Node *e, const Identifier &s) : expr(e), ident(s) { }
+ virtual void ref();
+ virtual bool deref();
+ Reference evaluateReference(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ Identifier ident;
+ };
+
+ class ArgumentListNode : public Node {
+ public:
+ // list is circular during construction. cracked in ArgumentsNode ctor
+ ArgumentListNode(Node *e) : list(this), expr(e) {}
+ ArgumentListNode(ArgumentListNode *l, Node *e)
+ : list(l->list), expr(e) { l->list = this; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ List evaluateList(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class ArgumentsNode;
+ ArgumentListNode *list;
+ Node *expr;
+ };
+
+ class ArgumentsNode : public Node {
+ public:
+ ArgumentsNode() : list(0) {}
+ ArgumentsNode(ArgumentListNode *l) : list(l->list) { l->list = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ List evaluateList(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ ArgumentListNode *list;
+ };
+
+ class NewExprNode : public Node {
+ public:
+ NewExprNode(Node *e) : expr(e), args(0L) {}
+ NewExprNode(Node *e, ArgumentsNode *a) : expr(e), args(a) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ ArgumentsNode *args;
+ };
+
+ class FunctionCallNode : public Node {
+ public:
+ FunctionCallNode(Node *e, ArgumentsNode *a) : expr(e), args(a) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ ArgumentsNode *args;
+ };
+
+ class PostfixNode : public Node {
+ public:
+ PostfixNode(Node *e, Operator o) : expr(e), oper(o) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ Operator oper;
+ };
+
+ class DeleteNode : public Node {
+ public:
+ DeleteNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class VoidNode : public Node {
+ public:
+ VoidNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class TypeOfNode : public Node {
+ public:
+ TypeOfNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class PrefixNode : public Node {
+ public:
+ PrefixNode(Operator o, Node *e) : oper(o), expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Operator oper;
+ Node *expr;
+ };
+
+ class UnaryPlusNode : public Node {
+ public:
+ UnaryPlusNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class NegateNode : public Node {
+ public:
+ NegateNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual double toNumber(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class BitwiseNotNode : public Node {
+ public:
+ BitwiseNotNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class LogicalNotNode : public Node {
+ public:
+ LogicalNotNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual bool toBoolean(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class MultNode : public Node {
+ public:
+ MultNode(Node *t1, Node *t2, char op) : term1(t1), term2(t2), oper(op) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *term1, *term2;
+ char oper;
+ };
+
+ class AddNode : public Node {
+ public:
+ AddNode(Node *t1, Node *t2, char op) : term1(t1), term2(t2), oper(op) {}
+
+ static Node* create(Node *t1, Node *t2, char op);
+
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *term1, *term2;
+ char oper;
+ };
+
+ class AppendStringNode : public Node {
+ public:
+ AppendStringNode(Node *t, const UString &s) : term(t), str(s) { }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *term;
+ UString str;
+ };
+
+ class ShiftNode : public Node {
+ public:
+ ShiftNode(Node *t1, Operator o, Node *t2)
+ : term1(t1), term2(t2), oper(o) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *term1, *term2;
+ Operator oper;
+ };
+
+ class RelationalNode : public Node {
+ public:
+ RelationalNode(Node *e1, Operator o, Node *e2) :
+ expr1(e1), expr2(e2), oper(o) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1, *expr2;
+ Operator oper;
+ };
+
+ class EqualNode : public Node {
+ public:
+ EqualNode(Node *e1, Operator o, Node *e2)
+ : expr1(e1), expr2(e2), oper(o) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1, *expr2;
+ Operator oper;
+ };
+
+ class BitOperNode : public Node {
+ public:
+ BitOperNode(Node *e1, Operator o, Node *e2) :
+ expr1(e1), expr2(e2), oper(o) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1, *expr2;
+ Operator oper;
+ };
+
+ /**
+ * expr1 && expr2, expr1 || expr2
+ */
+ class BinaryLogicalNode : public Node {
+ public:
+ BinaryLogicalNode(Node *e1, Operator o, Node *e2) :
+ expr1(e1), expr2(e2), oper(o) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1, *expr2;
+ Operator oper;
+ };
+
+ /**
+ * The ternary operator, "logical ? expr1 : expr2"
+ */
+ class ConditionalNode : public Node {
+ public:
+ ConditionalNode(Node *l, Node *e1, Node *e2) :
+ logical(l), expr1(e1), expr2(e2) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *logical, *expr1, *expr2;
+ };
+
+ class AssignNode : public Node {
+ public:
+ AssignNode(Node *l, Operator o, Node *e) : left(l), oper(o), expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *left;
+ Operator oper;
+ Node *expr;
+ };
+
+ class CommaNode : public Node {
+ public:
+ CommaNode(Node *e1, Node *e2) : expr1(e1), expr2(e2) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1, *expr2;
+ };
+
+ class StatListNode : public StatementNode {
+ public:
+ // list is circular during construction. cracked in CaseClauseNode ctor
+ StatListNode(StatementNode *s);
+ StatListNode(StatListNode *l, StatementNode *s);
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class CaseClauseNode;
+ StatementNode *statement;
+ StatListNode *list;
+ };
+
+ class AssignExprNode : public Node {
+ public:
+ AssignExprNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class VarDeclNode : public Node {
+ public:
+ enum Type { Variable, Constant };
+ VarDeclNode(const Identifier &id, AssignExprNode *in, Type t);
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Type varType;
+ Identifier ident;
+ AssignExprNode *init;
+ };
+
+ class VarDeclListNode : public Node {
+ public:
+ // list pointer is tail of a circular list, cracked in the ForNode/VarStatementNode ctor
+ VarDeclListNode(VarDeclNode *v) : list(this), var(v) {}
+ VarDeclListNode(VarDeclListNode *l, VarDeclNode *v)
+ : list(l->list), var(v) { l->list = this; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class ForNode;
+ friend class VarStatementNode;
+ VarDeclListNode *list;
+ VarDeclNode *var;
+ };
+
+ class VarStatementNode : public StatementNode {
+ public:
+ VarStatementNode(VarDeclListNode *l) : list(l->list) { l->list = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ VarDeclListNode *list;
+ };
+
+ class BlockNode : public StatementNode {
+ public:
+ BlockNode(SourceElementsNode *s);
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ protected:
+ SourceElementsNode *source;
+ };
+
+ class EmptyStatementNode : public StatementNode {
+ public:
+ EmptyStatementNode() { } // debug
+ virtual Completion execute(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ };
+
+ class ExprStatementNode : public StatementNode {
+ public:
+ ExprStatementNode(Node *e) : expr(e) { }
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class IfNode : public StatementNode {
+ public:
+ IfNode(Node *e, StatementNode *s1, StatementNode *s2)
+ : expr(e), statement1(s1), statement2(s2) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ StatementNode *statement1, *statement2;
+ };
+
+ class DoWhileNode : public StatementNode {
+ public:
+ DoWhileNode(StatementNode *s, Node *e) : statement(s), expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ StatementNode *statement;
+ Node *expr;
+ };
+
+ class WhileNode : public StatementNode {
+ public:
+ WhileNode(Node *e, StatementNode *s) : expr(e), statement(s) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ StatementNode *statement;
+ };
+
+ class ForNode : public StatementNode {
+ public:
+ ForNode(Node *e1, Node *e2, Node *e3, StatementNode *s) :
+ expr1(e1), expr2(e2), expr3(e3), statement(s) {}
+ ForNode(VarDeclListNode *e1, Node *e2, Node *e3, StatementNode *s) :
+ expr1(e1->list), expr2(e2), expr3(e3), statement(s) { e1->list = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr1, *expr2, *expr3;
+ StatementNode *statement;
+ };
+
+ class ForInNode : public StatementNode {
+ public:
+ ForInNode(Node *l, Node *e, StatementNode *s);
+ ForInNode(const Identifier &i, AssignExprNode *in, Node *e, StatementNode *s);
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ AssignExprNode *init;
+ Node *lexpr, *expr;
+ VarDeclNode *varDecl;
+ StatementNode *statement;
+ };
+
+ class ContinueNode : public StatementNode {
+ public:
+ ContinueNode() { }
+ ContinueNode(const Identifier &i) : ident(i) { }
+ virtual Completion execute(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ };
+
+ class BreakNode : public StatementNode {
+ public:
+ BreakNode() { }
+ BreakNode(const Identifier &i) : ident(i) { }
+ virtual Completion execute(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ };
+
+ class ReturnNode : public StatementNode {
+ public:
+ ReturnNode(Node *v) : value(v) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *value;
+ };
+
+ class WithNode : public StatementNode {
+ public:
+ WithNode(Node *e, StatementNode *s) : expr(e), statement(s) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ StatementNode *statement;
+ };
+
+ class CaseClauseNode : public Node {
+ public:
+ CaseClauseNode(Node *e) : expr(e), list(0) { }
+ CaseClauseNode(Node *e, StatListNode *l)
+ : expr(e), list(l->list) { l->list = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ Completion evalStatements(ExecState *exec) const;
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ StatListNode *list;
+ };
+
+ class ClauseListNode : public Node {
+ public:
+ // list is circular during construction. cracked in CaseBlockNode ctor
+ ClauseListNode(CaseClauseNode *c) : cl(c), nx(this) { }
+ ClauseListNode(ClauseListNode *n, CaseClauseNode *c)
+ : cl(c), nx(n->nx) { n->nx = this; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ CaseClauseNode *clause() const { return cl; }
+ ClauseListNode *next() const { return nx; }
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class CaseBlockNode;
+ CaseClauseNode *cl;
+ ClauseListNode *nx;
+ };
+
+ class CaseBlockNode: public Node {
+ public:
+ CaseBlockNode(ClauseListNode *l1, CaseClauseNode *d, ClauseListNode *l2);
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ Completion evalBlock(ExecState *exec, const Value& input) const;
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ ClauseListNode *list1;
+ CaseClauseNode *def;
+ ClauseListNode *list2;
+ };
+
+ class SwitchNode : public StatementNode {
+ public:
+ SwitchNode(Node *e, CaseBlockNode *b) : expr(e), block(b) { }
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ CaseBlockNode *block;
+ };
+
+ class LabelNode : public StatementNode {
+ public:
+ LabelNode(const Identifier &l, StatementNode *s) : label(l), statement(s) { }
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier label;
+ StatementNode *statement;
+ };
+
+ class ThrowNode : public StatementNode {
+ public:
+ ThrowNode(Node *e) : expr(e) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Node *expr;
+ };
+
+ class CatchNode : public StatementNode {
+ public:
+ CatchNode(const Identifier &i, StatementNode *b) : ident(i), block(b) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ Completion execute(ExecState *exec, const Value &arg);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ StatementNode *block;
+ };
+
+ class FinallyNode : public StatementNode {
+ public:
+ FinallyNode(StatementNode *b) : block(b) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ StatementNode *block;
+ };
+
+ class TryNode : public StatementNode {
+ public:
+ TryNode(StatementNode *b, CatchNode *c)
+ : block(b), _catch(c), _final(0) {}
+ TryNode(StatementNode *b, FinallyNode *f)
+ : block(b), _catch(0), _final(f) {}
+ TryNode(StatementNode *b, CatchNode *c, FinallyNode *f)
+ : block(b), _catch(c), _final(f) {}
+ virtual void ref();
+ virtual bool deref();
+ virtual Completion execute(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ StatementNode *block;
+ CatchNode *_catch;
+ FinallyNode *_final;
+ };
+
+ class ParameterNode : public Node {
+ public:
+ // list is circular during construction. cracked in FuncDecl/ExprNode ctor.
+ ParameterNode(const Identifier &i) : id(i), next(this) { }
+ ParameterNode(ParameterNode *list, const Identifier &i)
+ : id(i), next(list->next) { list->next = this; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ Identifier ident() const { return id; }
+ ParameterNode *nextParam() const { return next; }
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class FuncDeclNode;
+ friend class FuncExprNode;
+ Identifier id;
+ ParameterNode *next;
+ };
+
+ // inherited by ProgramNode
+ class FunctionBodyNode : public BlockNode {
+ public:
+ FunctionBodyNode(SourceElementsNode *s);
+ virtual void processFuncDecl(ExecState *exec);
+ };
+
+ class FuncDeclNode : public StatementNode {
+ public:
+ FuncDeclNode(const Identifier &i, FunctionBodyNode *b)
+ : ident(i), param(0), body(b) { }
+ FuncDeclNode(const Identifier &i, ParameterNode *p, FunctionBodyNode *b)
+ : ident(i), param(p->next), body(b) { p->next = 0; }
+ virtual void ref();
+ virtual bool deref();
+ Completion execute(ExecState* /*exec*/)
+ { /* empty */ return Completion(); }
+ void processFuncDecl(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ ParameterNode *param;
+ FunctionBodyNode *body;
+ };
+
+ class FuncExprNode : public Node {
+ public:
+ FuncExprNode(const Identifier &i, FunctionBodyNode *b)
+ : ident(i), param(0), body(b) { }
+ FuncExprNode(const Identifier &i, ParameterNode *p, FunctionBodyNode *b)
+ : ident(i), param(p->next), body(b) { p->next = 0; }
+ virtual void ref();
+ virtual bool deref();
+ virtual Value evaluate(ExecState *exec) const;
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ Identifier ident;
+ ParameterNode *param;
+ FunctionBodyNode *body;
+ };
+
+ // A linked list of source element nodes
+ class SourceElementsNode : public StatementNode {
+ public:
+ // list is circular until cracked in BlockNode (or subclass) ctor
+ SourceElementsNode(StatementNode *s1);
+ SourceElementsNode(SourceElementsNode *s1, StatementNode *s2);
+ virtual void ref();
+ virtual bool deref();
+ Completion execute(ExecState *exec);
+ virtual void processFuncDecl(ExecState *exec);
+ virtual void processVarDecls(ExecState *exec);
+ virtual void streamTo(SourceStream &s) const;
+ private:
+ friend class BlockNode;
+ StatementNode *element; // 'this' element
+ SourceElementsNode *elements; // pointer to next
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/nodes2string.cpp b/kjs/nodes2string.cpp
new file mode 100644
index 000000000..25ec0d712
--- /dev/null
+++ b/kjs/nodes2string.cpp
@@ -0,0 +1,629 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2002 Harri Porten (porten@kde.org)
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "nodes.h"
+
+namespace KJS {
+ /**
+ * A simple text streaming class that helps with code indentation.
+ */
+ class SourceStream {
+ public:
+ enum Format {
+ Endl, Indent, Unindent
+ };
+
+ UString toString() const { return str; }
+ SourceStream& operator<<(const Identifier &);
+ SourceStream& operator<<(const KJS::UString &);
+ SourceStream& operator<<(const char *);
+ SourceStream& operator<<(char);
+ SourceStream& operator<<(Format f);
+ SourceStream& operator<<(const Node *);
+ private:
+ UString str; /* TODO: buffer */
+ UString ind;
+ };
+}
+
+using namespace KJS;
+
+SourceStream& SourceStream::operator<<(char c)
+{
+ str += UString(c);
+ return *this;
+}
+
+SourceStream& SourceStream::operator<<(const char *s)
+{
+ str += UString(s);
+ return *this;
+}
+
+SourceStream& SourceStream::operator<<(const UString &s)
+{
+ str += s;
+ return *this;
+}
+
+SourceStream& SourceStream::operator<<(const Identifier &s)
+{
+ str += s.ustring();
+ return *this;
+}
+
+SourceStream& SourceStream::operator<<(const Node *n)
+{
+ if (n)
+ n->streamTo(*this);
+ return *this;
+}
+
+SourceStream& SourceStream::operator<<(Format f)
+{
+ switch (f) {
+ case Endl:
+ str += "\n" + ind;
+ break;
+ case Indent:
+ ind += " ";
+ break;
+ case Unindent:
+ ind = ind.substr(0, ind.size() - 2);
+ break;
+ }
+
+ return *this;
+}
+
+UString unescapeStr(UString str)
+{
+ UString unescaped = "";
+ int i = 0;
+ int copied = 0;
+ for (i = 0; i <= str.size(); i++) {
+ if (str[i] == '"') {
+ if (copied < i)
+ unescaped += str.substr(copied,i-copied);
+ copied = i+1;
+ unescaped += "\\\"";
+ }
+ }
+ if (copied < i)
+ unescaped += str.substr(copied,i-copied);
+ return unescaped;
+}
+
+UString Node::toCode() const
+{
+ SourceStream str;
+ streamTo(str);
+
+ return str.toString();
+}
+
+void NullNode::streamTo(SourceStream &s) const { s << "null"; }
+
+void BooleanNode::streamTo(SourceStream &s) const
+{
+ s << (val ? "true" : "false");
+}
+
+void NumberNode::streamTo(SourceStream &s) const { s << UString::from(val); }
+
+void StringNode::streamTo(SourceStream &s) const
+{
+ s << '"' << unescapeStr(val) << '"';
+}
+
+void RegExpNode::streamTo(SourceStream &s) const { s << "/" << pattern << "/" << flags; }
+
+void ThisNode::streamTo(SourceStream &s) const { s << "this"; }
+
+void ResolveNode::streamTo(SourceStream &s) const { s << ident; }
+
+void GroupNode::streamTo(SourceStream &s) const
+{
+ s << "(" << group << ")";
+}
+
+void ElementNode::streamTo(SourceStream &s) const
+{
+ for (const ElementNode *n = this; n; n = n->list) {
+ for (int i = 0; i < n->elision; i++)
+ s << ",";
+ s << n->node;
+ if ( n->list )
+ s << ",";
+ }
+}
+
+void ArrayNode::streamTo(SourceStream &s) const
+{
+ s << "[" << element;
+ for (int i = 0; i < elision; i++)
+ s << ",";
+ s << "]";
+}
+
+void ObjectLiteralNode::streamTo(SourceStream &s) const
+{
+ if (list)
+ s << "{ " << list << " }";
+ else
+ s << "{ }";
+}
+
+void PropertyValueNode::streamTo(SourceStream &s) const
+{
+ for (const PropertyValueNode *n = this; n; n = n->list)
+ s << n->name << ": " << n->assign;
+}
+
+void PropertyNode::streamTo(SourceStream &s) const
+{
+ if (str.isNull())
+ s << UString::from(numeric);
+ else
+ s << str;
+}
+
+void AccessorNode1::streamTo(SourceStream &s) const
+{
+ s << expr1 << "[" << expr2 << "]";
+}
+
+void AccessorNode2::streamTo(SourceStream &s) const
+{
+ s << expr << "." << ident;
+}
+
+void ArgumentListNode::streamTo(SourceStream &s) const
+{
+ s << expr;
+ for (ArgumentListNode *n = list; n; n = n->list)
+ s << ", " << n->expr;
+}
+
+void ArgumentsNode::streamTo(SourceStream &s) const
+{
+ s << "(" << list << ")";
+}
+
+void NewExprNode::streamTo(SourceStream &s) const
+{
+ s << "new " << expr << args;
+}
+
+void FunctionCallNode::streamTo(SourceStream &s) const
+{
+ s << expr << args;
+}
+
+void PostfixNode::streamTo(SourceStream &s) const
+{
+ s << expr;
+ if (oper == OpPlusPlus)
+ s << "++";
+ else
+ s << "--";
+}
+
+void DeleteNode::streamTo(SourceStream &s) const
+{
+ s << "delete " << expr;
+}
+
+void VoidNode::streamTo(SourceStream &s) const
+{
+ s << "void " << expr;
+}
+
+void TypeOfNode::streamTo(SourceStream &s) const
+{
+ s << "typeof " << expr;
+}
+
+void PrefixNode::streamTo(SourceStream &s) const
+{
+ s << (oper == OpPlusPlus ? "++" : "--") << expr;
+}
+
+void UnaryPlusNode::streamTo(SourceStream &s) const
+{
+ s << "+" << expr;
+}
+
+void NegateNode::streamTo(SourceStream &s) const
+{
+ s << "-" << expr;
+}
+
+void BitwiseNotNode::streamTo(SourceStream &s) const
+{
+ s << "~" << expr;
+}
+
+void LogicalNotNode::streamTo(SourceStream &s) const
+{
+ s << "!" << expr;
+}
+
+void MultNode::streamTo(SourceStream &s) const
+{
+ s << term1 << oper << term2;
+}
+
+void AddNode::streamTo(SourceStream &s) const
+{
+ s << term1 << oper << term2;
+}
+
+void AppendStringNode::streamTo(SourceStream &s) const
+{
+ s << term << "+" << '"' << unescapeStr(str) << '"';
+}
+
+void ShiftNode::streamTo(SourceStream &s) const
+{
+ s << term1;
+ if (oper == OpLShift)
+ s << "<<";
+ else if (oper == OpRShift)
+ s << ">>";
+ else
+ s << ">>>";
+ s << term2;
+}
+
+void RelationalNode::streamTo(SourceStream &s) const
+{
+ s << expr1;
+ switch (oper) {
+ case OpLess:
+ s << " < ";
+ break;
+ case OpGreater:
+ s << " > ";
+ break;
+ case OpLessEq:
+ s << " <= ";
+ break;
+ case OpGreaterEq:
+ s << " >= ";
+ break;
+ case OpInstanceOf:
+ s << " instanceof ";
+ break;
+ case OpIn:
+ s << " in ";
+ break;
+ default:
+ ;
+ }
+ s << expr2;
+}
+
+void EqualNode::streamTo(SourceStream &s) const
+{
+ s << expr1;
+ switch (oper) {
+ case OpEqEq:
+ s << " == ";
+ break;
+ case OpNotEq:
+ s << " != ";
+ break;
+ case OpStrEq:
+ s << " === ";
+ break;
+ case OpStrNEq:
+ s << " !== ";
+ break;
+ default:
+ ;
+ }
+ s << expr2;
+}
+
+void BitOperNode::streamTo(SourceStream &s) const
+{
+ s << expr1;
+ if (oper == OpBitAnd)
+ s << " & ";
+ else if (oper == OpBitXOr)
+ s << " ^ ";
+ else
+ s << " | ";
+ s << expr2;
+}
+
+void BinaryLogicalNode::streamTo(SourceStream &s) const
+{
+ s << expr1 << (oper == OpAnd ? " && " : " || ") << expr2;
+}
+
+void ConditionalNode::streamTo(SourceStream &s) const
+{
+ s << logical << " ? " << expr1 << " : " << expr2;
+}
+
+void AssignNode::streamTo(SourceStream &s) const
+{
+ s << left;
+ const char *opStr;
+ switch (oper) {
+ case OpEqual:
+ opStr = " = ";
+ break;
+ case OpMultEq:
+ opStr = " *= ";
+ break;
+ case OpDivEq:
+ opStr = " /= ";
+ break;
+ case OpPlusEq:
+ opStr = " += ";
+ break;
+ case OpMinusEq:
+ opStr = " -= ";
+ break;
+ case OpLShift:
+ opStr = " <<= ";
+ break;
+ case OpRShift:
+ opStr = " >>= ";
+ break;
+ case OpURShift:
+ opStr = " >>= ";
+ break;
+ case OpAndEq:
+ opStr = " &= ";
+ break;
+ case OpXOrEq:
+ opStr = " ^= ";
+ break;
+ case OpOrEq:
+ opStr = " |= ";
+ break;
+ case OpModEq:
+ opStr = " %= ";
+ break;
+ default:
+ opStr = " ?= ";
+ }
+ s << opStr << expr;
+}
+
+void CommaNode::streamTo(SourceStream &s) const
+{
+ s << expr1 << ", " << expr2;
+}
+
+void StatListNode::streamTo(SourceStream &s) const
+{
+ for (const StatListNode *n = this; n; n = n->list)
+ s << n->statement;
+}
+
+void AssignExprNode::streamTo(SourceStream &s) const
+{
+ s << " = " << expr;
+}
+
+void VarDeclNode::streamTo(SourceStream &s) const
+{
+ s << ident << init;
+}
+
+void VarDeclListNode::streamTo(SourceStream &s) const
+{
+ s << var;
+ for (VarDeclListNode *n = list; n; n = n->list)
+ s << ", " << n->var;
+}
+
+void VarStatementNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "var " << list << ";";
+}
+
+void BlockNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "{" << SourceStream::Indent
+ << source << SourceStream::Unindent << SourceStream::Endl << "}";
+}
+
+void EmptyStatementNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << ";";
+}
+
+void ExprStatementNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << expr << ";";
+}
+
+void IfNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "if (" << expr << ")" << SourceStream::Indent
+ << statement1 << SourceStream::Unindent;
+ if (statement2)
+ s << SourceStream::Endl << "else" << SourceStream::Indent
+ << statement2 << SourceStream::Unindent;
+}
+
+void DoWhileNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "do " << SourceStream::Indent
+ << statement << SourceStream::Unindent << SourceStream::Endl
+ << "while (" << expr << ");";
+}
+
+void WhileNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "while (" << expr << ")" << SourceStream::Indent
+ << statement << SourceStream::Unindent;
+}
+
+void ForNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "for ("
+ << expr1 // TODO: doesn't properly do "var i = 0"
+ << "; " << expr2
+ << "; " << expr3
+ << ")" << SourceStream::Indent << statement << SourceStream::Unindent;
+}
+
+void ForInNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "for (";
+ if (varDecl)
+ s << "var " << varDecl;
+ if (init)
+ s << " = " << init;
+ s << " in " << expr << ")" << SourceStream::Indent
+ << statement << SourceStream::Unindent;
+}
+
+void ContinueNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "continue";
+ if (!ident.isNull())
+ s << " " << ident;
+ s << ";";
+}
+
+void BreakNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "break";
+ if (!ident.isNull())
+ s << " " << ident;
+ s << ";";
+}
+
+void ReturnNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "return";
+ if (value)
+ s << " " << value;
+ s << ";";
+}
+
+void WithNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "with (" << expr << ") "
+ << statement;
+}
+
+void CaseClauseNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl;
+ if (expr)
+ s << "case " << expr;
+ else
+ s << "default";
+ s << ":" << SourceStream::Indent;
+ if (list)
+ s << list;
+ s << SourceStream::Unindent;
+}
+
+void ClauseListNode::streamTo(SourceStream &s) const
+{
+ for (const ClauseListNode *n = this; n; n = n->next())
+ s << n->clause();
+}
+
+void CaseBlockNode::streamTo(SourceStream &s) const
+{
+ for (const ClauseListNode *n = list1; n; n = n->next())
+ s << n->clause();
+ if (def)
+ s << def;
+ for (const ClauseListNode *n = list2; n; n = n->next())
+ s << n->clause();
+}
+
+void SwitchNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "switch (" << expr << ") {"
+ << SourceStream::Indent << block << SourceStream::Unindent
+ << SourceStream::Endl << "}";
+}
+
+void LabelNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << label << ":" << SourceStream::Indent
+ << statement << SourceStream::Unindent;
+}
+
+void ThrowNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "throw " << expr << ";";
+}
+
+void CatchNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "catch (" << ident << ")" << block;
+}
+
+void FinallyNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "finally " << block;
+}
+
+void TryNode::streamTo(SourceStream &s) const
+{
+ s << SourceStream::Endl << "try " << block
+ << _catch
+ << _final;
+}
+
+void ParameterNode::streamTo(SourceStream &s) const
+{
+ s << id;
+ for (ParameterNode *n = next; n; n = n->next)
+ s << ", " << n->id;
+}
+
+void FuncDeclNode::streamTo(SourceStream &s) const {
+ s << SourceStream::Endl << "function " << ident << "(";
+ if (param)
+ s << param;
+ s << ")" << body;
+}
+
+void FuncExprNode::streamTo(SourceStream &s) const
+{
+ s << "function " << "("
+ << param
+ << ")" << body;
+}
+
+void SourceElementsNode::streamTo(SourceStream &s) const
+{
+ for (const SourceElementsNode *n = this; n; n = n->elements)
+ s << n->element;
+}
+
diff --git a/kjs/number_object.cpp b/kjs/number_object.cpp
new file mode 100644
index 000000000..0d6698142
--- /dev/null
+++ b/kjs/number_object.cpp
@@ -0,0 +1,512 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2003 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "number_object.h"
+#include "error_object.h"
+#include "dtoa.h"
+
+#include "number_object.lut.h"
+
+#include <assert.h>
+#include <math.h>
+
+using namespace KJS;
+
+// ------------------------------ NumberInstanceImp ----------------------------
+
+const ClassInfo NumberInstanceImp::info = {"Number", 0, 0, 0};
+
+NumberInstanceImp::NumberInstanceImp(ObjectImp *proto)
+ : ObjectImp(proto)
+{
+}
+// ------------------------------ NumberPrototypeImp ---------------------------
+
+// ECMA 15.7.4
+
+NumberPrototypeImp::NumberPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto,
+ FunctionPrototypeImp *funcProto)
+ : NumberInstanceImp(objProto)
+{
+ Value protect(this);
+ setInternalValue(NumberImp::zero());
+
+ // The constructor will be added later, after NumberObjectImp has been constructed
+
+ putDirect(toStringPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToString,
+ 1,toStringPropertyName),DontEnum);
+ putDirect(toLocaleStringPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToLocaleString,
+ 0,toLocaleStringPropertyName),DontEnum);
+ putDirect(valueOfPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ValueOf,
+ 0,valueOfPropertyName),DontEnum);
+ putDirect("toFixed", new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToFixed,
+ 1,"toFixed"),DontEnum);
+ putDirect("toExponential",new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToExponential,
+ 1,"toExponential"),DontEnum);
+ putDirect("toPrecision",new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToPrecision,
+ 1,"toPrecision"),DontEnum);
+}
+
+
+// ------------------------------ NumberProtoFuncImp ---------------------------
+
+NumberProtoFuncImp::NumberProtoFuncImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+ ident = _ident;
+}
+
+
+bool NumberProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+static UString integer_part_noexp(double d)
+{
+ int decimalPoint;
+ int signDummy;
+ char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &signDummy, NULL);
+ int length = strlen(result);
+
+ // sign for non-zero, negative numbers
+ UString str = d < 0 ? "-" : "";
+ if (decimalPoint == 9999) {
+ str += UString(result);
+ } else if (decimalPoint <= 0) {
+ str += UString("0");
+ } else {
+ char *buf;
+
+ if (length <= decimalPoint) {
+ buf = (char*)malloc(decimalPoint+1);
+ strcpy(buf,result);
+ memset(buf+length,'0',decimalPoint-length);
+ } else {
+ buf = (char*)malloc(decimalPoint+1);
+ strncpy(buf,result,decimalPoint);
+ }
+
+ buf[decimalPoint] = '\0';
+ str += UString(buf);
+ free(buf);
+ }
+
+ kjs_freedtoa(result);
+
+ return str;
+}
+
+static UString char_sequence(char c, int count)
+{
+ char *buf = (char*)malloc(count+1);
+ memset(buf,c,count);
+ buf[count] = '\0';
+ UString s(buf);
+ free(buf);
+ return s;
+}
+
+// ECMA 15.7.4.2 - 15.7.4.7
+Value NumberProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ Value result;
+
+ // no generic function. "this" has to be a Number object
+ KJS_CHECK_THIS( NumberInstanceImp, thisObj );
+
+ // execute "toString()" or "valueOf()", respectively
+ Value v = thisObj.internalValue();
+ switch (id) {
+ case ToString: {
+ int radix = 10;
+ if (!args.isEmpty() && args[0].type() != UndefinedType)
+ radix = args[0].toInteger(exec);
+ if (radix < 2 || radix > 36 || radix == 10)
+ result = String(v.toString(exec));
+ else {
+ 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];
+ double x = v.toNumber(exec);
+ if (isNaN(x) || isInf(x))
+ return String(UString::from(x));
+ // apply algorithm on absolute value. add sign later.
+ bool neg = false;
+ if (x < 0.0) {
+ neg = true;
+ x = -x;
+ }
+ // convert integer portion
+ double f = floor(x);
+ double d = f;
+ char *dot = s + sizeof(s) / 2;
+ char *p = dot;
+ *p = '\0';
+ do {
+ *--p = digits[int(fmod(d, double(radix)))];
+ d /= radix;
+ } while ((d <= -1.0 || d >= 1.0) && p > s);
+ // any decimal fraction ?
+ d = x - f;
+ const double eps = 0.001; // TODO: guessed. base on radix ?
+ if (d < -eps || d > eps) {
+ *dot++ = '.';
+ do {
+ d *= radix;
+ *dot++ = digits[int(d)];
+ d -= int(d);
+ } while ((d < -eps || d > eps) && dot - s < int(sizeof(s)) - 1);
+ *dot = '\0';
+ }
+ // add sign if negative
+ if (neg)
+ *--p = '-';
+ result = String(p);
+ }
+ break;
+ }
+ case ToLocaleString: /* TODO */
+ result = String(v.toString(exec));
+ break;
+ case ValueOf:
+ result = Number(v.toNumber(exec));
+ break;
+ case ToFixed:
+ {
+ // FIXME: firefox works for all values, not just 0..20. This includes
+ // NaN, infinity, undefined, etc. This is just a hack to pass our regression
+ // suite.
+ Value fractionDigits = args[0];
+ int f = -1;
+ double fd = fractionDigits.toNumber(exec);
+ if (isNaN(fd)) {
+ f = 0;
+ } else if (!isInf(fd)) {
+ f = int(fd);
+ }
+ if (f < 0 || f > 20) {
+ Object err = Error::create(exec,RangeError);
+ exec->setException(err);
+ return err;
+ }
+
+ double x = v.toNumber(exec);
+ if (isNaN(x))
+ return String("NaN");
+
+ UString s = "";
+ if (x < 0) {
+ s += "-";
+ x = -x;
+ }
+
+ if (x >= 1e21)
+ return String(s+UString::from(x));
+
+ double n = floor(x*pow(10.0,f));
+ if (fabs(n/pow(10.0,f)-x) > fabs((n+1)/pow(10.0,f)-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 += "0";
+ m = z + m;
+ k = f + 1;
+ assert(k == m.size());
+ }
+ if (k-f < m.size())
+ return String(s+m.substr(0,k-f)+"."+m.substr(k-f));
+ else
+ return String(s+m.substr(0,k-f));
+ }
+ case ToExponential: {
+ double x = v.toNumber(exec);
+
+ if (isNaN(x) || isInf(x))
+ return String(UString::from(x));
+
+ int f = 1;
+ Value fractionDigits = args[0];
+ if (args.size() > 0) {
+ f = fractionDigits.toInteger(exec);
+ if (f < 0 || f > 20) {
+ Object err = Error::create(exec,RangeError);
+ exec->setException(err);
+ return err;
+ }
+ }
+
+ int decimalAdjust = 0;
+ if (!fractionDigits.isA(UndefinedType)) {
+ double logx = floor(log10(fabs(x)));
+ x /= pow(10.0,logx);
+ double fx = floor(x*pow(10.0,f))/pow(10.0,f);
+ double cx = ceil(x*pow(10.0,f))/pow(10.0,f);
+
+ if (fabs(fx-x) < fabs(cx-x))
+ x = fx;
+ else
+ x = cx;
+
+ decimalAdjust = int(logx);
+ }
+
+ char buf[80];
+ int decimalPoint;
+ int sign;
+
+ if (isNaN(x))
+ return String("NaN");
+
+ char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
+ int length = strlen(result);
+ decimalPoint += decimalAdjust;
+
+ int i = 0;
+ if (sign) {
+ buf[i++] = '-';
+ }
+
+ if (decimalPoint == 999) {
+ strcpy(buf + i, result);
+ } else {
+ buf[i++] = result[0];
+
+ if (fractionDigits.isA(UndefinedType))
+ f = length-1;
+
+ if (length > 1 && f > 0) {
+ buf[i++] = '.';
+ int haveFDigits = length-1;
+ if (f < haveFDigits) {
+ strncpy(buf+i,result+1, f);
+ i += f;
+ }
+ else {
+ strcpy(buf+i,result+1);
+ i += length-1;
+ for (int j = 0; j < f-haveFDigits; j++)
+ 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 * -1;
+ }
+ if (exponential >= 100) {
+ buf[i++] = '0' + exponential / 100;
+ }
+ if (exponential >= 10) {
+ buf[i++] = '0' + (exponential % 100) / 10;
+ }
+ buf[i++] = '0' + exponential % 10;
+ buf[i++] = '\0';
+ }
+
+ assert(i <= 80);
+
+ kjs_freedtoa(result);
+
+ return String(UString(buf));
+ }
+ case ToPrecision:
+ {
+ int e = 0;
+ UString m;
+
+ int p = args[0].toInteger(exec);
+ double x = v.toNumber(exec);
+ if (args[0].isA(UndefinedType) || isNaN(x) || isInf(x))
+ return String(v.toString(exec));
+
+ UString s = "";
+ if (x < 0) {
+ s = "-";
+ x = -x;
+ }
+
+ if (p < 1 || p > 21) {
+ Object err = Error::create(exec, RangeError,
+ "toPrecision() argument must be between 1 and 21");
+ exec->setException(err);
+ return err;
+ }
+
+ if (x != 0) {
+ // suggestions for a better algorithm welcome!
+ e = int(log10(x));
+ double n = floor(x/pow(10.0,e-p+1));
+ if (n < pow(10.0,p-1)) {
+ // first guess was not good
+ e = e - 1;
+ n = floor(x/pow(10.0,e-p+1));
+ if (n >= pow(10.0,p)) {
+ // violated constraint. try something else.
+ n = pow(10.0,p-1);
+ e = int(log10(x/n)) + p - 1;
+ }
+ }
+
+ if (fabs((n+1)*pow(10.0,e-p+1)-x) < fabs(n*pow(10.0,e-p+1)-x))
+ n++;
+ assert(pow(10.0,p-1) <= n);
+ assert(n < pow(10.0,p));
+
+ m = integer_part_noexp(n);
+ if (e < -6 || e >= p) {
+ if (m.size() > 1)
+ m = m.substr(0,1)+"."+m.substr(1);
+ if (e >= 0)
+ return String(s+m+"e+"+UString::from(e));
+ else
+ return String(s+m+"e-"+UString::from(-e));
+ }
+ }
+ else {
+ m = char_sequence('0',p);
+ e = 0;
+ }
+
+ if (e == p-1) {
+ return String(s+m);
+ }
+ else if (e >= 0) {
+ if (e+1 < m.size())
+ return String(s+m.substr(0,e+1)+"."+m.substr(e+1));
+ else
+ return String(s+m.substr(0,e+1));
+ }
+ else {
+ return String(s+"0."+char_sequence('0',-(e+1))+m);
+ }
+ }
+ }
+
+ return result;
+}
+
+// ------------------------------ NumberObjectImp ------------------------------
+
+const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
+
+/* 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*/,
+ FunctionPrototypeImp *funcProto,
+ NumberPrototypeImp *numberProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ // Number.Prototype
+ putDirect(prototypePropertyName, numberProto, DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
+}
+
+Value NumberObjectImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ return lookupGetValue<NumberObjectImp, InternalFunctionImp>( exec, propertyName, &numberTable, this );
+}
+
+Value NumberObjectImp::getValueProperty(ExecState *, int token) const
+{
+ // ECMA 15.7.3
+ switch(token) {
+ case NaNValue:
+ return Number(NaN);
+ case NegInfinity:
+ return Number(-Inf);
+ case PosInfinity:
+ return Number(Inf);
+ case MaxValue:
+ return Number(1.7976931348623157E+308);
+ case MinValue:
+ return Number(5E-324);
+ }
+ return Null();
+}
+
+bool NumberObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+
+// ECMA 15.7.1
+Object NumberObjectImp::construct(ExecState *exec, const List &args)
+{
+ ObjectImp *proto = exec->lexicalInterpreter()->builtinNumberPrototype().imp();
+ Object obj(new NumberInstanceImp(proto));
+
+ Number n;
+ if (args.isEmpty())
+ n = Number(0);
+ else
+ n = args[0].toNumber(exec);
+
+ obj.setInternalValue(n);
+
+ return obj;
+}
+
+bool NumberObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.7.2
+Value NumberObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ if (args.isEmpty())
+ return Number(0);
+ else
+ return Number(args[0].toNumber(exec));
+}
diff --git a/kjs/number_object.h b/kjs/number_object.h
new file mode 100644
index 000000000..b48d3c3c0
--- /dev/null
+++ b/kjs/number_object.h
@@ -0,0 +1,99 @@
+// -*- 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 "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class NumberInstanceImp : public ObjectImp {
+ public:
+ NumberInstanceImp(ObjectImp *proto);
+
+ 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 NumberPrototypeImp : public NumberInstanceImp {
+ public:
+ NumberPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto,
+ FunctionPrototypeImp *funcProto);
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Number.prototype object
+ */
+ class NumberProtoFuncImp : public InternalFunctionImp {
+ public:
+ NumberProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { ToString, ToLocaleString, ValueOf, ToFixed, ToExponential, ToPrecision };
+ private:
+ int id;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "Number" property
+ */
+ class NumberObjectImp : public InternalFunctionImp {
+ public:
+ NumberObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ NumberPrototypeImp *numberProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ Value get(ExecState *exec, const Identifier &p) const;
+ Value getValueProperty(ExecState *exec, int token) const;
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ enum { NaNValue, NegInfinity, PosInfinity, MaxValue, MinValue };
+
+ Completion execute(const List &);
+ Object construct(const List &);
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/object.cpp b/kjs/object.cpp
new file mode 100644
index 000000000..fba1e0257
--- /dev/null
+++ b/kjs/object.cpp
@@ -0,0 +1,563 @@
+// -*- 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "lookup.h"
+#include "reference_list.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "internal.h"
+#include "collector.h"
+#include "operations.h"
+#include "error_object.h"
+#include "nodes.h"
+
+using namespace KJS;
+
+// ------------------------------ Object ---------------------------------------
+
+Object Object::dynamicCast(const Value &v)
+{
+ if (!v.isValid() || v.type() != ObjectType)
+ return Object(0);
+
+ return Object(static_cast<ObjectImp*>(v.imp()));
+}
+
+Value Object::call(ExecState *exec, Object &thisObj, const List &args)
+{
+#if KJS_MAX_STACK > 0
+ static int depth = 0; // sum of all concurrent interpreters
+ if (++depth > KJS_MAX_STACK) {
+#ifndef NDEBUG
+ fprintf(stderr, "Exceeded maximum function call depth\n");
+#endif
+ int saveDepth = depth - 1;
+ Object err = Error::create(exec, RangeError,
+ "Exceeded maximum function call depth.");
+ depth = depth - 10; //Give some room for the debugger to operate,
+ //so if it tries to examine things we don't get here again
+ exec->setException(err);
+ depth = saveDepth;
+ return err;
+ }
+#endif
+
+ Value ret = static_cast<ObjectImp*>(rep)->call(exec,thisObj,args);
+
+#if KJS_MAX_STACK > 0
+ --depth;
+#endif
+
+ return ret;
+}
+
+// ------------------------------ ObjectImp ------------------------------------
+
+ObjectImp::ObjectImp(const Object &proto)
+ : _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
+{
+ //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
+}
+
+ObjectImp::ObjectImp(ObjectImp *proto)
+ : _proto(proto), _internalValue(0L)
+{
+}
+
+ObjectImp::ObjectImp()
+{
+ //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
+ _proto = NullImp::staticNull;
+ _internalValue = 0L;
+}
+
+ObjectImp::~ObjectImp()
+{
+ //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
+}
+
+void ObjectImp::mark()
+{
+ //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
+ ValueImp::mark();
+
+ if (_proto && !_proto->marked())
+ _proto->mark();
+
+ _prop.mark();
+
+ if (_internalValue && !_internalValue->marked())
+ _internalValue->mark();
+
+ _scope.mark();
+}
+
+const ClassInfo *ObjectImp::classInfo() const
+{
+ return 0;
+}
+
+bool ObjectImp::inherits(const ClassInfo *info) const
+{
+ if (!info)
+ return false;
+
+ const ClassInfo *ci = classInfo();
+ if (!ci)
+ return false;
+
+ while (ci && ci != info)
+ ci = ci->parentClass;
+
+ return (ci == info);
+}
+
+Type ObjectImp::type() const
+{
+ return ObjectType;
+}
+
+Value ObjectImp::prototype() const
+{
+ return Value(_proto);
+}
+
+void ObjectImp::setPrototype(const Value &proto)
+{
+ _proto = proto.imp();
+}
+
+UString ObjectImp::className() const
+{
+ const ClassInfo *ci = classInfo();
+ if ( ci )
+ return ci->className;
+ return "Object";
+}
+
+Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ ValueImp *imp = getDirect(propertyName);
+ if (imp)
+ return Value(imp);
+
+ Object proto = Object::dynamicCast(prototype());
+
+ // non-standard netscape extension
+ if (propertyName == specialPrototypePropertyName) {
+ if (!proto.isValid())
+ return Null();
+ else
+ return Value(proto);
+ }
+
+ if (!proto.isValid())
+ return Undefined();
+
+ return proto.get(exec,propertyName);
+}
+
+Value ObjectImp::getPropertyByIndex(ExecState *exec,
+ unsigned propertyName) const
+{
+ return get(exec, Identifier::from(propertyName));
+}
+
+// ECMA 8.6.2.2
+void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
+ const Value &value, int attr)
+{
+ assert(value.isValid());
+
+ // non-standard netscape extension
+ if (propertyName == specialPrototypePropertyName) {
+ setPrototype(value);
+ return;
+ }
+
+ /* TODO: check for write permissions directly w/o this call */
+ /* Doesn't look very easy with the PropertyMap API - David */
+ // putValue() is used for JS assignemnts. It passes no attribute.
+ // Assume that a C++ implementation knows what it is doing
+ // and let it override the canPut() check.
+ if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
+#ifdef KJS_VERBOSE
+ fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
+#endif
+ return;
+ }
+
+ _prop.put(propertyName,value.imp(),attr);
+}
+
+// delme
+void ObjectImp::putPropertyByIndex(ExecState *exec, unsigned propertyName,
+ const Value &value, int attr)
+{
+ put(exec, Identifier::from(propertyName), value, attr);
+}
+
+// ECMA 8.6.2.3
+bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
+{
+ int attributes;
+ ValueImp *v = _prop.get(propertyName, attributes);
+ if (v)
+ return!(attributes & ReadOnly);
+
+ // Look in the static hashtable of properties
+ const HashEntry* e = findPropertyHashEntry(propertyName);
+ if (e)
+ return !(e->attr & ReadOnly);
+
+ // Don't look in the prototype here. We can always put an override
+ // in the object, even if the prototype has a ReadOnly property.
+ return true;
+}
+
+// ECMA 8.6.2.4
+bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
+{
+ if (_prop.get(propertyName))
+ return true;
+
+ // Look in the static hashtable of properties
+ if (findPropertyHashEntry(propertyName))
+ return true;
+
+ // non-standard netscape extension
+ if (propertyName == specialPrototypePropertyName)
+ return true;
+
+ // Look in the prototype
+ Object proto = Object::dynamicCast(prototype());
+ return proto.isValid() && proto.hasProperty(exec,propertyName);
+}
+
+bool ObjectImp::hasPropertyByIndex(ExecState *exec, unsigned propertyName) const
+{
+ return hasProperty(exec, Identifier::from(propertyName));
+}
+
+// ECMA 8.6.2.5
+bool ObjectImp::deleteProperty(ExecState * /*exec*/, const Identifier &propertyName)
+{
+ int attributes;
+ ValueImp *v = _prop.get(propertyName, attributes);
+ if (v) {
+ if ((attributes & DontDelete))
+ return false;
+ _prop.remove(propertyName);
+ 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 ObjectImp::deletePropertyByIndex(ExecState *exec, unsigned propertyName)
+{
+ return deleteProperty(exec, Identifier::from(propertyName));
+}
+
+void ObjectImp::deleteAllProperties( ExecState * )
+{
+ _prop.clear();
+}
+
+// ECMA 8.6.2.6
+Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
+{
+ if (hint != StringType && hint != NumberType) {
+ /* Prefer String for Date objects */
+ if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
+ hint = StringType;
+ else
+ hint = NumberType;
+ }
+
+ Value v;
+ if (hint == StringType)
+ v = get(exec,toStringPropertyName);
+ else
+ v = get(exec,valueOfPropertyName);
+
+ if (v.type() == ObjectType) {
+ Object o = Object(static_cast<ObjectImp*>(v.imp()));
+ if (o.implementsCall()) { // spec says "not primitive type" but ...
+ Object thisObj = Object(const_cast<ObjectImp*>(this));
+ Value def = o.call(exec,thisObj,List::empty());
+ Type defType = def.type();
+ if (defType == UnspecifiedType || defType == UndefinedType ||
+ defType == NullType || defType == BooleanType ||
+ defType == StringType || defType == NumberType) {
+ return def;
+ }
+ }
+ }
+
+ if (hint == StringType)
+ v = get(exec,valueOfPropertyName);
+ else
+ v = get(exec,toStringPropertyName);
+
+ if (v.type() == ObjectType) {
+ Object o = Object(static_cast<ObjectImp*>(v.imp()));
+ if (o.implementsCall()) { // spec says "not primitive type" but ...
+ Object thisObj = Object(const_cast<ObjectImp*>(this));
+ Value def = o.call(exec,thisObj,List::empty());
+ Type defType = def.type();
+ if (defType == UnspecifiedType || defType == UndefinedType ||
+ defType == NullType || defType == BooleanType ||
+ defType == StringType || defType == NumberType) {
+ return def;
+ }
+ }
+ }
+
+ Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
+ exec->setException(err);
+ return err;
+}
+
+const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
+{
+ const ClassInfo *info = classInfo();
+ while (info) {
+ if (info->propHashTable) {
+ const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
+ if (e)
+ return e;
+ }
+ info = info->parentClass;
+ }
+ return 0L;
+}
+
+bool ObjectImp::implementsConstruct() const
+{
+ return false;
+}
+
+Object ObjectImp::construct(ExecState* /*exec*/, const List &/*args*/)
+{
+ assert(false);
+ return Object(0);
+}
+
+bool ObjectImp::implementsCall() const
+{
+ return false;
+}
+
+Value ObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
+{
+ assert(false);
+ return Object(0);
+}
+
+bool ObjectImp::implementsHasInstance() const
+{
+ return false;
+}
+
+Boolean ObjectImp::hasInstance(ExecState* /*exec*/, const Value &/*value*/)
+{
+ assert(false);
+ return Boolean(false);
+}
+
+ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
+{
+ ReferenceList list;
+ if (_proto && _proto->dispatchType() == ObjectType && recursive)
+ list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
+
+ _prop.addEnumerablesToReferenceList(list, Object(this));
+
+ // 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->soffset && !(e->attr & DontEnum) )
+ list.append(Reference(this, &info->propHashTable->sbase[e->soffset])); /// ######### check for duplicates with the propertymap
+ }
+ }
+ info = info->parentClass;
+ }
+
+ return list;
+}
+
+Value ObjectImp::internalValue() const
+{
+ return Value(_internalValue);
+}
+
+void ObjectImp::setInternalValue(const Value &v)
+{
+ _internalValue = v.imp();
+}
+
+void ObjectImp::setInternalValue(ValueImp *v)
+{
+ v->setGcAllowed();
+ _internalValue = v;
+}
+
+Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
+{
+ return defaultValue(exec,preferredType);
+}
+
+bool ObjectImp::toBoolean(ExecState* /*exec*/) const
+{
+ return true;
+}
+
+double ObjectImp::toNumber(ExecState *exec) const
+{
+ Value prim = toPrimitive(exec,NumberType);
+ if (exec->hadException()) // should be picked up soon in nodes.cpp
+ return 0.0;
+ return prim.toNumber(exec);
+}
+
+UString ObjectImp::toString(ExecState *exec) const
+{
+ Value prim = toPrimitive(exec,StringType);
+ if (exec->hadException()) // should be picked up soon in nodes.cpp
+ return "";
+ return prim.toString(exec);
+}
+
+Object ObjectImp::toObject(ExecState * /*exec*/) const
+{
+ return Object(const_cast<ObjectImp*>(this));
+}
+
+void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
+{
+ value->setGcAllowed();
+ _prop.put(propertyName, value, attr);
+}
+
+void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
+{
+ _prop.put(propertyName, NumberImp::create(value), attr);
+}
+
+void ObjectImp::setFunctionName(const Identifier &propertyName)
+{
+ if (inherits(&InternalFunctionImp::info))
+ static_cast<InternalFunctionImp*>(this)->setName(propertyName);
+}
+
+// ------------------------------ 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;
+
+Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
+ int lineno, int sourceId)
+{
+#ifdef KJS_VERBOSE
+ // message could be 0L. Don't enable this on Solaris ;)
+ fprintf(stderr, "WARNING: KJS %s: %s\n", errorNames[errtype], message);
+#endif
+
+ Object cons;
+
+ switch (errtype) {
+ case EvalError:
+ cons = exec->lexicalInterpreter()->builtinEvalError();
+ break;
+ case RangeError:
+ cons = exec->lexicalInterpreter()->builtinRangeError();
+ break;
+ case ReferenceError:
+ cons = exec->lexicalInterpreter()->builtinReferenceError();
+ break;
+ case SyntaxError:
+ cons = exec->lexicalInterpreter()->builtinSyntaxError();
+ break;
+ case TypeError:
+ cons = exec->lexicalInterpreter()->builtinTypeError();
+ break;
+ case URIError:
+ cons = exec->lexicalInterpreter()->builtinURIError();
+ break;
+ default:
+ cons = exec->lexicalInterpreter()->builtinError();
+ break;
+ }
+
+ if (!message)
+ message = errorNames[errtype];
+ List args;
+ args.append(String(message));
+ Object err = Object::dynamicCast(cons.construct(exec,args));
+
+ if (lineno != -1)
+ err.put(exec, "line", Number(lineno));
+ if (sourceId != -1)
+ err.put(exec, "sourceId", Number(sourceId));
+
+ return err;
+
+/*
+#ifndef NDEBUG
+ const char *msg = err.get(messagePropertyName).toString().value().ascii();
+ if (l >= 0)
+ fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
+ else
+ fprintf(stderr, "KJS: %s. %s\n", estr, msg);
+#endif
+
+ return err;
+*/
+}
+
diff --git a/kjs/object.h b/kjs/object.h
new file mode 100644
index 000000000..ca795460c
--- /dev/null
+++ b/kjs/object.h
@@ -0,0 +1,726 @@
+// -*- 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 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_
+
+// Objects
+
+#include "value.h"
+#include "types.h"
+#include "reference_list.h"
+#include "identifier.h"
+#include "property_map.h"
+#include "scope_chain.h"
+
+namespace KJS {
+
+ class ObjectImpPrivate;
+ class PropertyMap;
+ class HashTable;
+ struct HashEntry;
+ class ListImp;
+
+ /** Attributes (only applicable to the Object type).
+ * See ECMA 262-3 8.6.1
+ */
+ 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 by pass checks
+ Function = 1 << 5 }; ///< property is a function - only used by static hashtables
+
+ /**
+ * 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;
+ /**
+ * Reserved for future extension.
+ */
+ void *dummy;
+ };
+
+ /**
+ * Represents an Object. This is a wrapper for ObjectImp
+ */
+ class KJS_EXPORT Object : public Value {
+ public:
+ Object() { }
+ explicit Object(ObjectImp *v);
+
+ ObjectImp *imp() const;
+
+ const ClassInfo *classInfo() const;
+ bool inherits(const ClassInfo *cinfo) const;
+
+ /**
+ * Converts a Value into an Object. If the value's type is not ObjectType,
+ * a null object will be returned (i.e. one with it's internal pointer set
+ * to 0). If you do not know for sure whether the value is of type
+ * ObjectType, you should check the isValid() methods afterwards before
+ * calling any methods on the Object.
+ *
+ * @return The value converted to an object
+ */
+ static Object dynamicCast(const Value &v);
+
+ /**
+ * 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
+ */
+ Value prototype() const;
+
+ /**
+ * Returns the class name of the object
+ *
+ * See ECMA 8.6.2
+ *
+ * @return The object's class name
+ */
+ 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
+ */
+ Value get(ExecState *exec, const Identifier &propertyName) const;
+ Value get(ExecState *exec, unsigned propertyName) const;
+
+ /**
+ * 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 value The value to set
+ * @param attr The Attribute value for the property
+ */
+ void put(ExecState *exec, const Identifier &propertyName,
+ const Value &value, int attr = None);
+ void put(ExecState *exec, unsigned propertyName,
+ const Value &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
+ */
+ bool canPut(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 *exec, const Identifier &propertyName) const;
+ bool hasProperty(ExecState *exec, unsigned propertyName) 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.
+ */
+ bool deleteProperty(ExecState *exec, const Identifier &propertyName);
+ 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.
+ */
+ Value defaultValue(ExecState *exec, Type 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
+ */
+ 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 &amp; initialized object
+ */
+ Object construct(ExecState *exec, const List &args);
+
+ /**
+ * 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
+ */
+ 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
+ */
+ Value call(ExecState *exec, Object &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
+ */
+ 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
+ */
+ Boolean hasInstance(ExecState *exec, const Value &value);
+
+ /**
+ * Returns the scope of this object. This is used when execution declared
+ * functions - the execution context for the function is initialized with
+ * extra object in it's scope. An example of this is functions declared
+ * inside other functions:
+ *
+ * \code
+ * function f() {
+ *
+ * function b() {
+ * return prototype;
+ * }
+ *
+ * var x = 4;
+ * // do some stuff
+ * }
+ * f.prototype = new String();
+ * \endcode
+ *
+ * When the function f.b is executed, its scope will include properties of
+ * f. So in the example above the return value of f.b() would be the new
+ * String object that was assigned to f.prototype.
+ *
+ * @return The function's scope
+ */
+ const ScopeChain &scope() const;
+ void setScope(const ScopeChain &s);
+
+ /**
+ * Returns a List of References to all the properties of the object. Used
+ * in "for x in y" statements. The list is created new, so it can be freely
+ * modified without affecting the object's properties. It should be deleted
+ * by the caller.
+ *
+ * Subclasses can override this method in ObjectImpl to provide the
+ * appearance of
+ * having extra properties other than those set specifically with put().
+ *
+ * @param exec The current execution state
+ * @param recursive Whether or not properties in the object's prototype
+ * chain should be
+ * included in the list.
+ * @return A List of References to properties of the object.
+ **/
+ ReferenceList propList(ExecState *exec, bool recursive = true);
+
+ /**
+ * 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
+ */
+ Value internalValue() const;
+
+ /**
+ * Sets the internal value of the object
+ *
+ * @see internalValue()
+ *
+ * @param v The new internal value
+ */
+ void setInternalValue(const Value &v);
+ };
+
+ inline Object Value::toObject(ExecState *exec) const { return rep->dispatchToObject(exec); }
+
+ class KJS_EXPORT ObjectImp : public ValueImp {
+ friend class ObjectProtoFuncImp;
+ public:
+ /**
+ * Creates a new ObjectImp with the specified prototype
+ *
+ * @param proto The prototype
+ */
+ ObjectImp(const Object &proto);
+ ObjectImp(ObjectImp *proto);
+
+ /**
+ * Creates a new ObjectImp with a prototype of Null()
+ * (that is, the ECMAScript "null" value, not a null Object).
+ *
+ */
+ ObjectImp();
+
+ virtual ~ObjectImp();
+
+ virtual void mark();
+
+ Type 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 ObjectImp {
+ * virtual const ClassInfo *classInfo() const { return &info; }
+ * static const ClassInfo info;
+ * // ...
+ * };
+ *
+ * class FooImp : public ObjectImp {
+ * virtual const ClassInfo *classInfo() const { return &info; }
+ * static const ClassInfo info;
+ * // ...
+ * };
+ * \endcode
+ *
+ * And in your source file:
+ *
+ * \code
+ * const ClassInfo BarImp::info = {0, 0, 0}; // no parent class
+ * const ClassInfo FooImp::info = {&BarImp::info, 0, 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 ObjectImp 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)
+
+ /**
+ * Implementation of the [[Prototype]] internal property (implemented by
+ * all Objects)
+ *
+ * @see Object::prototype()
+ */
+ Value prototype() const;
+ void setPrototype(const Value &proto);
+
+ /**
+ * 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.
+ *
+ * @see Object::className()
+ */
+ virtual UString className() const;
+
+ /**
+ * Implementation of the [[Get]] internal property (implemented by all
+ * Objects)
+ *
+ * @see Object::get()
+ */
+ // [[Get]] - must be implemented by all Objects
+ virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+ virtual Value getPropertyByIndex(ExecState *exec,
+ unsigned propertyName) const;
+
+ /**
+ * Implementation of the [[Put]] internal property (implemented by all
+ * Objects)
+ *
+ * @see Object::put()
+ */
+ virtual void put(ExecState *exec, const Identifier &propertyName,
+ const Value &value, int attr = None);
+ virtual void putPropertyByIndex(ExecState *exec, unsigned propertyName,
+ const Value &value, int attr = None);
+
+ /**
+ * Implementation of the [[CanPut]] internal property (implemented by all
+ * Objects)
+ *
+ * @see Object::canPut()
+ */
+ virtual bool canPut(ExecState *exec, const Identifier &propertyName) const;
+
+ /**
+ * Implementation of the [[HasProperty]] internal property (implemented by
+ * all Objects)
+ *
+ * @see Object::hasProperty()
+ */
+ virtual bool hasProperty(ExecState *exec,
+ const Identifier &propertyName) const;
+ virtual bool hasPropertyByIndex(ExecState *exec, unsigned propertyName) const;
+
+ /**
+ * Implementation of the [[Delete]] internal property (implemented by all
+ * Objects)
+ *
+ * @see Object::deleteProperty()
+ */
+ virtual bool deleteProperty(ExecState *exec,
+ const Identifier &propertyName);
+ virtual bool deletePropertyByIndex(ExecState *exec, unsigned propertyName);
+
+ /**
+ * Remove all properties from this object.
+ * This doesn't take DontDelete into account, and isn't in the ECMA spec.
+ * It's simply a quick way to remove everything before destroying.
+ */
+ void deleteAllProperties(ExecState *);
+
+ /**
+ * Implementation of the [[DefaultValue]] internal property (implemented by
+ * all Objects)
+ *
+ * @see Object::defaultValue()
+ */
+ virtual Value defaultValue(ExecState *exec, Type hint) const;
+
+ virtual bool implementsConstruct() const;
+ /**
+ * Implementation of the [[Construct]] internal property
+ *
+ * @see Object::construct()
+ */
+ virtual Object construct(ExecState *exec, const List &args);
+
+ virtual bool implementsCall() const;
+ /**
+ * Implementation of the [[Call]] internal property
+ *
+ * @see Object::call()
+ */
+ virtual Value call(ExecState *exec, Object &thisObj,
+ const List &args);
+
+ virtual bool implementsHasInstance() const;
+ /**
+ * Implementation of the [[HasInstance]] internal property
+ *
+ * @see Object::hasInstance()
+ */
+ virtual Boolean hasInstance(ExecState *exec, const Value &value);
+
+ /**
+ * Implementation of the [[Scope]] internal property
+ *
+ * @see Object::scope()
+ */
+ const ScopeChain &scope() const { return _scope; }
+ void setScope(const ScopeChain &s) { _scope = s; }
+
+ virtual ReferenceList propList(ExecState *exec, bool recursive = true);
+
+ Value internalValue() const;
+ void setInternalValue(const Value &v);
+ void setInternalValue(ValueImp *v);
+
+ Value toPrimitive(ExecState *exec,
+ Type preferredType = UnspecifiedType) const;
+ bool toBoolean(ExecState *exec) const;
+ double toNumber(ExecState *exec) const;
+ UString toString(ExecState *exec) const;
+ Object toObject(ExecState *exec) const;
+
+ // This get method only looks at the property map.
+ // A bit like hasProperty(recursive=false), this doesn't go to the prototype.
+ // 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)
+ ValueImp *getDirect(const Identifier& propertyName) const
+ { return _prop.get(propertyName); }
+ void putDirect(const Identifier &propertyName, ValueImp *value, int attr = 0);
+ void putDirect(const Identifier &propertyName, int value, int attr = 0);
+
+ /**
+ * Sets the name of the function, if this is an InternalFunctionImp object.
+ * (calling InternalFunctionImp::setName)
+ */
+ void setFunctionName(const Identifier &propertyName);
+
+ protected:
+ PropertyMap _prop;
+ private:
+ const HashEntry* findPropertyHashEntry( const Identifier& propertyName ) const;
+ ObjectImpPrivate *_od;
+ ValueImp *_proto;
+ ValueImp *_internalValue;
+ ScopeChain _scope;
+ };
+
+ /**
+ * 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 KJS_EXPORT Error {
+ public:
+ /**
+ * Factory method for error objects.
+ *
+ * @param exec The current execution state
+ * @param errtype Type of error.
+ * @param message Optional error message.
+ * @param lineno Optional line number.
+ * @param sourceId Optional source id.
+ */
+ static Object create(ExecState *exec, ErrorType errtype = GeneralError,
+ const char *message = 0, int lineno = -1,
+ int sourceId = -1);
+
+ /**
+ * Array of error names corresponding to ErrorType
+ */
+ static const char * const * const errorNames;
+ };
+
+ inline Object::Object(ObjectImp *v) : Value(v) { }
+
+ inline ObjectImp *Object::imp() const { return static_cast<ObjectImp*>(rep); }
+
+ inline const ClassInfo *Object::classInfo() const
+ { return imp()->classInfo(); }
+
+ inline bool Object::inherits(const ClassInfo *cinfo) const
+ { return imp()->inherits(cinfo); }
+
+ inline Value Object::prototype() const
+ { return Value(imp()->prototype()); }
+
+ inline UString Object::className() const
+ { return imp()->className(); }
+
+ inline Value Object::get(ExecState *exec, const Identifier &propertyName) const
+ { return imp()->get(exec,propertyName); }
+
+ inline Value Object::get(ExecState *exec, unsigned propertyName) const
+ { return imp()->getPropertyByIndex(exec, propertyName); }
+
+ inline void Object::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
+ { imp()->put(exec,propertyName,value,attr); }
+
+ inline void Object::put(ExecState *exec, unsigned propertyName, const Value &value, int attr)
+ { imp()->putPropertyByIndex(exec, propertyName, value, attr); }
+
+ inline bool Object::canPut(ExecState *exec, const Identifier &propertyName) const
+ { return imp()->canPut(exec,propertyName); }
+
+ inline bool Object::hasProperty(ExecState *exec, const Identifier &propertyName) const
+ { return imp()->hasProperty(exec, propertyName); }
+
+ inline bool Object::hasProperty(ExecState *exec, unsigned propertyName) const
+ { return imp()->hasPropertyByIndex(exec, propertyName); }
+
+ inline bool Object::deleteProperty(ExecState *exec, const Identifier &propertyName)
+ { return imp()->deleteProperty(exec,propertyName); }
+
+ inline bool Object::deleteProperty(ExecState *exec, unsigned propertyName)
+ { return imp()->deletePropertyByIndex(exec, propertyName); }
+
+ inline Value Object::defaultValue(ExecState *exec, Type hint) const
+ { return imp()->defaultValue(exec,hint); }
+
+ inline bool Object::implementsConstruct() const
+ { return imp()->implementsConstruct(); }
+
+ inline Object Object::construct(ExecState *exec, const List &args)
+ { return imp()->construct(exec,args); }
+
+ inline bool Object::implementsCall() const
+ { return imp()->implementsCall(); }
+
+ inline bool Object::implementsHasInstance() const
+ { return imp()->implementsHasInstance(); }
+
+ inline Boolean Object::hasInstance(ExecState *exec, const Value &value)
+ { return imp()->hasInstance(exec,value); }
+
+ inline const ScopeChain &Object::scope() const
+ { return imp()->scope(); }
+
+ inline void Object::setScope(const ScopeChain &s)
+ { imp()->setScope(s); }
+
+ inline ReferenceList Object::propList(ExecState *exec, bool recursive)
+ { return imp()->propList(exec,recursive); }
+
+ inline Value Object::internalValue() const
+ { return imp()->internalValue(); }
+
+ inline void Object::setInternalValue(const Value &v)
+ { imp()->setInternalValue(v); }
+
+} // namespace
+
+#endif // _KJS_OBJECT_H_
diff --git a/kjs/object_object.cpp b/kjs/object_object.cpp
new file mode 100644
index 000000000..e17835df6
--- /dev/null
+++ b/kjs/object_object.cpp
@@ -0,0 +1,204 @@
+// -*- 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
+ *
+ */
+
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "object_object.h"
+#include "function_object.h"
+#include "lookup.h"
+#include <stdio.h>
+#include <assert.h>
+
+using namespace KJS;
+
+// ------------------------------ ObjectPrototypeImp --------------------------------
+
+ObjectPrototypeImp::ObjectPrototypeImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto)
+ : ObjectImp() // [[Prototype]] is Null()
+{
+ Value protect(this);
+ putDirect(toStringPropertyName, new ObjectProtoFuncImp(exec,funcProto,ObjectProtoFuncImp::ToString,
+ 0, toStringPropertyName), DontEnum);
+ putDirect(toLocaleStringPropertyName, new ObjectProtoFuncImp(exec,funcProto,ObjectProtoFuncImp::ToLocaleString,
+ 0, toLocaleStringPropertyName), DontEnum);
+ putDirect(valueOfPropertyName, new ObjectProtoFuncImp(exec,funcProto,ObjectProtoFuncImp::ValueOf,
+ 0, valueOfPropertyName), DontEnum);
+ putDirect("hasOwnProperty", new ObjectProtoFuncImp(exec,funcProto,ObjectProtoFuncImp::HasOwnProperty,
+ 1,"hasOwnProperty"),DontEnum);
+ putDirect("isPrototypeOf", new ObjectProtoFuncImp(exec,funcProto,ObjectProtoFuncImp::IsPrototypeOf,
+ 1,"isPrototypeOf"),DontEnum);
+ putDirect("propertyIsEnumerable", new ObjectProtoFuncImp(exec,funcProto,ObjectProtoFuncImp::PropertyIsEnumerable,
+ 1,"propertyIsEnumerable"),DontEnum);
+
+#ifndef KJS_PURE_ECMA // standard compliance location is the Global object
+ // see http://www.devguru.com/Technologies/ecmascript/quickref/object.html
+ put(exec, "eval",
+ Object(new GlobalFuncImp(exec, funcProto,GlobalFuncImp::Eval, 1, "eval")),
+ DontEnum);
+#endif
+}
+
+// ------------------------------ ObjectProtoFuncImp --------------------------------
+
+ObjectProtoFuncImp::ObjectProtoFuncImp(ExecState * /*exec*/,
+ FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+ ident = _ident;
+}
+
+
+bool ObjectProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.2.4.2 + 15.2.4.3
+
+Value ObjectProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ switch (id) {
+ case ToString:
+ // fall through
+ case ToLocaleString:
+ return String("[object "+thisObj.className()+"]");
+ case ValueOf:
+ return thisObj;
+ case HasOwnProperty: {
+ // Same as hasProperty() but without checking the prototype
+ Identifier propertyName(args[0].toString(exec));
+ Value tempProto(thisObj.imp()->prototype());
+ thisObj.imp()->setPrototype(Value());
+ bool exists = thisObj.hasProperty(exec,propertyName);
+ thisObj.imp()->setPrototype(tempProto);
+ return Value(exists ? BooleanImp::staticTrue : BooleanImp::staticFalse);
+ }
+ case IsPrototypeOf: {
+ Value v = args[0];
+ for (; v.isValid() && v.isA(ObjectType); v = Object::dynamicCast(v).prototype()) {
+ if (v.imp() == thisObj.imp())
+ return Value(BooleanImp::staticTrue);
+ }
+ return Value(BooleanImp::staticFalse);
+ }
+ case PropertyIsEnumerable: {
+ Identifier propertyName(args[0].toString(exec));
+ ObjectImp *obj = static_cast<ObjectImp*>(thisObj.imp());
+
+ int attributes;
+ ValueImp *v = obj->_prop.get(propertyName,attributes);
+ if (v)
+ return Value((attributes & DontEnum) ?
+ BooleanImp::staticFalse : BooleanImp::staticTrue);
+
+ if (propertyName == specialPrototypePropertyName)
+ return Value(BooleanImp::staticFalse);
+
+ const HashEntry *entry = obj->findPropertyHashEntry(propertyName);
+ return Value((entry && !(entry->attr & DontEnum)) ?
+ BooleanImp::staticTrue : BooleanImp::staticFalse);
+ }
+ }
+
+ return Undefined();
+}
+
+// ------------------------------ ObjectObjectImp --------------------------------
+
+ObjectObjectImp::ObjectObjectImp(ExecState * /*exec*/,
+ ObjectPrototypeImp *objProto,
+ FunctionPrototypeImp *funcProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ // ECMA 15.2.3.1
+ putDirect(prototypePropertyName, objProto, DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
+}
+
+
+bool ObjectObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.2.2
+Object ObjectObjectImp::construct(ExecState *exec, const List &args)
+{
+ // if no arguments have been passed ...
+ if (args.isEmpty()) {
+ Object proto = exec->lexicalInterpreter()->builtinObjectPrototype();
+ Object result(new ObjectImp(proto));
+ return result;
+ }
+
+ Value arg = *(args.begin());
+ Object obj = Object::dynamicCast(arg);
+ if (obj.isValid())
+ return obj;
+
+ switch (arg.type()) {
+ case StringType:
+ case BooleanType:
+ case NumberType:
+ return arg.toObject(exec);
+ default:
+ assert(!"unhandled switch case in ObjectConstructor");
+ case NullType:
+ case UndefinedType:
+ Object proto = exec->lexicalInterpreter()->builtinObjectPrototype();
+ return Object(new ObjectImp(proto));
+ }
+}
+
+bool ObjectObjectImp::implementsCall() const
+{
+ return true;
+}
+
+Value ObjectObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ Value result;
+
+ List argList;
+ // Construct a new Object
+ if (args.isEmpty()) {
+ result = construct(exec,argList);
+ } else {
+ Value arg = args[0];
+ if (arg.type() == NullType || arg.type() == UndefinedType) {
+ argList.append(arg);
+ result = construct(exec,argList);
+ } else
+ result = arg.toObject(exec);
+ }
+ return result;
+}
+
diff --git a/kjs/object_object.h b/kjs/object_object.h
new file mode 100644
index 000000000..c3192107d
--- /dev/null
+++ b/kjs/object_object.h
@@ -0,0 +1,84 @@
+// -*- 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 _OBJECT_OBJECT_H_
+#define _OBJECT_OBJECT_H_
+
+#include "internal.h"
+#include "function.h"
+
+namespace KJS {
+
+ class FunctionPrototypeImp;
+
+ /**
+ * @internal
+ *
+ * The initial value of Object.prototype (and thus all objects created
+ * with the Object constructor
+ */
+ class ObjectPrototypeImp : public ObjectImp {
+ public:
+ ObjectPrototypeImp(ExecState *exec, FunctionPrototypeImp *funcProto);
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Object.prototype object
+ */
+ class ObjectProtoFuncImp : public InternalFunctionImp {
+ public:
+ ObjectProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { ToString, ToLocaleString, ValueOf, HasOwnProperty,
+ IsPrototypeOf, PropertyIsEnumerable };
+
+ private:
+ int id;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "Object" property
+ */
+ class ObjectObjectImp : public InternalFunctionImp {
+ public:
+
+ ObjectObjectImp(ExecState *exec,
+ ObjectPrototypeImp *objProto,
+ FunctionPrototypeImp *funcProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/operations.cpp b/kjs/operations.cpp
new file mode 100644
index 000000000..63c1e669e
--- /dev/null
+++ b/kjs/operations.cpp
@@ -0,0 +1,265 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifndef HAVE_FLOAT_H /* just for !Windows */
+#define HAVE_FLOAT_H 0
+#define HAVE_FUNC__FINITE 0
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+
+// For declaration of isinf on Sun C++
+#ifdef __SUNPRO_CC
+#include <sunmath.h>
+#endif
+
+#ifndef HAVE_FUNC_ISINF
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#endif /* HAVE_FUNC_ISINF */
+
+#if HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+#include "operations.h"
+#include "object.h"
+
+using namespace KJS;
+
+bool KJS::isNaN(double d)
+{
+#ifdef HAVE_FUNC_ISNAN
+ return isnan(d);
+#elif defined HAVE_FLOAT_H
+ return _isnan(d) != 0;
+#else
+ return !(d == d);
+#endif
+}
+
+bool KJS::isInf(double d)
+{
+#if defined(HAVE_FUNC_ISINF)
+ return isinf(d);
+#elif HAVE_FUNC_FINITE
+ return finite(d) == 0 && d == d;
+#elif HAVE_FUNC__FINITE
+ return _finite(d) == 0 && d == d;
+#else
+ return false;
+#endif
+}
+
+bool KJS::isPosInf(double d)
+{
+#if defined(HAVE_FUNC_ISINF)
+ return (isinf(d) == 1);
+#elif HAVE_FUNC_FINITE
+ return finite(d) == 0 && d == d; // ### can we distinguish between + and - ?
+#elif HAVE_FUNC__FINITE
+ return _finite(d) == 0 && d == d; // ###
+#else
+ return false;
+#endif
+}
+
+bool KJS::isNegInf(double d)
+{
+#if defined(HAVE_FUNC_ISINF)
+ return (isinf(d) == -1);
+#elif HAVE_FUNC_FINITE
+ return finite(d) == 0 && d == d; // ###
+#elif HAVE_FUNC__FINITE
+ return _finite(d) == 0 && d == d; // ###
+#else
+ return false;
+#endif
+}
+
+// ECMA 11.9.3
+bool KJS::equal(ExecState *exec, const Value& v1, const Value& v2)
+{
+ Type t1 = v1.type();
+ Type t2 = v2.type();
+
+ if (t1 == t2) {
+ if (t1 == UndefinedType || t1 == NullType)
+ return true;
+ if (t1 == NumberType)
+ {
+ double d1 = v1.toNumber(exec);
+ double d2 = v2.toNumber(exec);
+ if ( isNaN( d1 ) || isNaN( d2 ) )
+ return false;
+ return ( d1 == d2 ); /* TODO: +0, -0 ? */
+ }
+ if (t1 == StringType)
+ return (v1.toString(exec) == v2.toString(exec));
+ if (t1 == BooleanType)
+ return (v1.toBoolean(exec) == v2.toBoolean(exec));
+
+ // types are Object
+ return (v1.imp() == v2.imp());
+ }
+
+ // different types
+ if ((t1 == NullType && t2 == UndefinedType) || (t1 == UndefinedType && t2 == NullType))
+ return true;
+ if (t1 == NumberType && t2 == StringType) {
+ Number n2 = v2.toNumber(exec);
+ return equal(exec,v1, n2);
+ }
+ if ((t1 == StringType && t2 == NumberType) || t1 == BooleanType) {
+ Number n1 = v1.toNumber(exec);
+ return equal(exec,n1, v2);
+ }
+ if (t2 == BooleanType) {
+ Number n2 = v2.toNumber(exec);
+ return equal(exec,v1, n2);
+ }
+ if ((t1 == StringType || t1 == NumberType) && t2 >= ObjectType) {
+ Value p2 = v2.toPrimitive(exec);
+ return equal(exec,v1, p2);
+ }
+ if (t1 >= ObjectType && (t2 == StringType || t2 == NumberType)) {
+ Value p1 = v1.toPrimitive(exec);
+ return equal(exec,p1, v2);
+ }
+
+ return false;
+}
+
+bool KJS::strictEqual(ExecState *exec, const Value &v1, const Value &v2)
+{
+ Type t1 = v1.type();
+ Type 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 (isNaN(n1) || isNaN(n2))
+ return false;
+ if (n1 == n2)
+ return true;
+ /* TODO: +0 and -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.imp() == v2.imp())
+ return true;
+ /* TODO: joined objects */
+
+ return false;
+}
+
+int KJS::relation(ExecState *exec, const Value& v1, const Value& v2)
+{
+ Value p1 = v1.toPrimitive(exec,NumberType);
+ Value p2 = v2.toPrimitive(exec,NumberType);
+
+ if (p1.type() == StringType && p2.type() == StringType)
+ return p1.toString(exec) < p2.toString(exec) ? 1 : 0;
+
+ double n1 = p1.toNumber(exec);
+ double n2 = p2.toNumber(exec);
+ if ( isNaN( n1 ) || isNaN( n2 ) )
+ return -1; // means undefined
+ if (n1 == n2)
+ return 0;
+ /* TODO: +0, -0 */
+ if ( isPosInf( n1 ) )
+ return 0;
+ if ( isPosInf( n2 ) )
+ return 1;
+ if ( isNegInf( n2 ) )
+ return 0;
+ if ( isNegInf( n1 ) )
+ return 1;
+ return (n1 < n2) ? 1 : 0;
+}
+
+int KJS::maxInt(int d1, int d2)
+{
+ return (d1 > d2) ? d1 : d2;
+}
+
+int KJS::minInt(int d1, int d2)
+{
+ return (d1 < d2) ? d1 : d2;
+}
+
+// ECMA 11.6
+Value KJS::add(ExecState *exec, const Value &v1, const Value &v2, char oper)
+{
+ // exception for the Date exception in defaultValue()
+ Type preferred = oper == '+' ? UnspecifiedType : NumberType;
+ Value p1 = v1.toPrimitive(exec, preferred);
+ Value p2 = v2.toPrimitive(exec, preferred);
+
+ if ((p1.type() == StringType || p2.type() == StringType) && oper == '+') {
+ UString s1 = p1.toString(exec);
+ UString s2 = p2.toString(exec);
+
+ return String(s1 + s2);
+ }
+
+ double n1 = p1.toNumber(exec);
+ double n2 = p2.toNumber(exec);
+
+ if (oper == '+')
+ return Number(n1 + n2);
+ else
+ return Number(n1 - n2);
+}
+
+// ECMA 11.5
+Value KJS::mult(ExecState *exec, const Value &v1, const Value &v2, char oper)
+{
+ double n1 = v1.toNumber(exec);
+ double n2 = v2.toNumber(exec);
+
+ double result;
+
+ if (oper == '*')
+ result = n1 * n2;
+ else if (oper == '/')
+ result = n1 / n2;
+ else
+ result = fmod(n1, n2);
+
+ return Number(result);
+}
diff --git a/kjs/operations.h b/kjs/operations.h
new file mode 100644
index 000000000..4579b11dc
--- /dev/null
+++ b/kjs/operations.h
@@ -0,0 +1,79 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_OPERATIONS_H_
+#define _KJS_OPERATIONS_H_
+
+#include "value.h"
+
+namespace KJS {
+
+ class ExecState;
+
+ /**
+ * @return True if d is not a number (platform support required).
+ */
+ bool isNaN(double d);
+ /**
+ * @return True if d is infinite (platform support required).
+ */
+ bool isInf(double d);
+ bool isPosInf(double d);
+ bool isNegInf(double d);
+ bool equal(ExecState *exec, const Value& v1, const Value& v2);
+ bool strictEqual(ExecState *exec, const Value &v1, const Value &v2);
+ /**
+ * This operator performs an abstract relational comparison of the two
+ * arguments that can be of arbitrary type. If possible, conversions to the
+ * string or number type will take place before the comparison.
+ *
+ * @return 1 if v1 is "less-than" v2, 0 if the relation is "greater-than-or-
+ * equal". -1 if the result is undefined.
+ */
+ int relation(ExecState *exec, const Value& v1, const Value& v2);
+ int maxInt(int d1, int d2);
+ int minInt(int d1, int d2);
+ /**
+ * Additive operator. Either performs an addition or substraction of v1
+ * and v2.
+ * @param exec execution state.
+ * @param v1 First operand.
+ * @param v2 Second operand.
+ * @param oper '+' or '-' for an addition or substraction, respectively.
+ * @return The result of the operation.
+ */
+ Value add(ExecState *exec, const Value &v1, const Value &v2, char oper);
+ /**
+ * Multiplicative operator. Either multiplies/divides v1 and v2 or
+ * calculates the remainder from an division.
+ * @param exec execution state.
+ * @param v1 First operand.
+ * @param v2 Second operand.
+ * @param oper '*', '/' or '%' for a multiplication, division or
+ * modulo operation.
+ * @return The result of the operation.
+ */
+ Value mult(ExecState *exec, const Value &v1, const Value &v2, char oper);
+
+}
+
+#endif
diff --git a/kjs/property_map.cpp b/kjs/property_map.cpp
new file mode 100644
index 000000000..bd9e823de
--- /dev/null
+++ b/kjs/property_map.cpp
@@ -0,0 +1,656 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "property_map.h"
+
+#include "object.h"
+#include "reference_list.h"
+
+#include <assert.h>
+
+#define DEBUG_PROPERTIES 0
+#define DO_CONSISTENCY_CHECK 0
+#define DUMP_STATISTICS 0
+#define USE_SINGLE_ENTRY 1
+
+// At the time I added USE_SINGLE_ENTRY, the optimization still gave a 1.5%
+// performance boost to the iBench JavaScript benchmark so I didn't remove it.
+
+#if !DO_CONSISTENCY_CHECK
+#define checkConsistency() ((void)0)
+#endif
+
+namespace KJS {
+
+#if DUMP_STATISTICS
+
+static int numProbes;
+static int numCollisions;
+
+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);
+}
+
+#endif
+
+struct PropertyMapHashTable
+{
+ int sizeMask;
+ int size;
+ int keyCount;
+
+ // gets initialized in expand, an array that stores insert order of a particular hash
+ int *hashToIndex; // NOTE: is one based 1,2,3 etc..
+
+ // keeps trac on how many insertions we have made, cant use keyCount because delete a key in the middle messes things
+ int indexCount;
+
+ PropertyMapHashTableEntry entries[1];
+};
+
+class SavedProperty {
+public:
+ Identifier key;
+ Value value;
+ int attributes;
+};
+
+SavedProperties::SavedProperties() : _count(0), _properties(0) { }
+
+SavedProperties::~SavedProperties()
+{
+ delete [] _properties;
+}
+
+// Algorithm concepts from Algorithms in C++, Sedgewick.
+
+PropertyMap::PropertyMap() : _table(0)
+{
+}
+
+PropertyMap::~PropertyMap()
+{
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (key)
+ key->deref();
+#endif
+ return;
+ }
+
+ for (int i = 0; i < _table->size; i++) {
+ UString::Rep *key = _table->entries[i].key;
+ if (key)
+ key->deref();
+ }
+ // fredrik added to cleanup sortorder
+ if (_table)
+ delete [] _table->hashToIndex;
+
+ free(_table);
+}
+
+void PropertyMap::clear()
+{
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (key) {
+ key->deref();
+ _singleEntry.key = 0;
+ }
+#endif
+ return;
+ }
+
+ for (int i = 0; i < _table->size; i++) {
+ UString::Rep *key = _table->entries[i].key;
+ if (key) {
+ key->deref();
+ _table->entries[i].key = 0;
+ }
+ }
+ _table->keyCount = 0;
+}
+
+inline int PropertyMap::hash(const UString::Rep *s) const
+{
+ return s->hash() & _table->sizeMask;
+}
+
+ValueImp *PropertyMap::get(const Identifier &name, int &attributes) const
+{
+ assert(!name.isNull());
+
+ UString::Rep *rep = name._ustring.rep;
+
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (rep == key) {
+ attributes = _singleEntry.attributes;
+ return _singleEntry.value;
+ }
+#endif
+ return 0;
+ }
+
+ int i = hash(rep);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[i].key && _table->entries[i].key != rep;
+#endif
+ while (UString::Rep *key = _table->entries[i].key) {
+ if (rep == key) {
+ attributes = _table->entries[i].attributes;
+ return _table->entries[i].value;
+ }
+ i = (i + 1) & _table->sizeMask;
+ }
+ return 0;
+}
+
+ValueImp *PropertyMap::get(const Identifier &name) const
+{
+ assert(!name.isNull());
+
+ UString::Rep *rep = name._ustring.rep;
+
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (rep == key)
+ return _singleEntry.value;
+#endif
+ return 0;
+ }
+
+ int i = hash(rep);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[i].key && _table->entries[i].key != rep;
+#endif
+ while (UString::Rep *key = _table->entries[i].key) {
+ if (rep == key)
+ return _table->entries[i].value;
+ i = (i + 1) & _table->sizeMask;
+ }
+ return 0;
+}
+
+#if DEBUG_PROPERTIES
+static void printAttributes(int attributes)
+{
+ if (attributes == 0)
+ printf ("None ");
+ if (attributes & ReadOnly)
+ printf ("ReadOnly ");
+ if (attributes & DontEnum)
+ printf ("DontEnum ");
+ if (attributes & DontDelete)
+ printf ("DontDelete ");
+ if (attributes & Internal)
+ printf ("Internal ");
+ if (attributes & Function)
+ printf ("Function ");
+}
+#endif
+
+void PropertyMap::put(const Identifier &name, ValueImp *value, int attributes)
+{
+ assert(!name.isNull());
+ assert(value != 0);
+
+#if DO_CONSISTENCY_CHECK // speed, why call a stub if we dont need to??
+ checkConsistency();
+#endif
+
+ UString::Rep *rep = name._ustring.rep;
+
+#if DEBUG_PROPERTIES
+ printf("adding property %s, attributes = 0x%08x (", name.ascii(), attributes);
+ printAttributes(attributes);
+ printf(")\n");
+#endif
+
+#if USE_SINGLE_ENTRY
+ if (!_table) {
+ UString::Rep *key = _singleEntry.key;
+ if (key) {
+ if (rep == key) {
+ _singleEntry.value = value;
+ return;
+ }
+ } else {
+ rep->ref();
+ _singleEntry.key = rep;
+ _singleEntry.value = value;
+ _singleEntry.attributes = attributes;
+ checkConsistency();
+ return;
+ }
+ }
+#endif
+ if (!_table || _table->keyCount * 2 >= _table->size)
+ expand();
+ int i = hash(rep);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[i].key && _table->entries[i].key != rep;
+#endif
+ while (UString::Rep *key = _table->entries[i].key) {
+ if (rep == key) {
+ // Put a new value in an existing hash table entry.
+ _table->entries[i].value = value;
+ // Attributes are intentionally not updated.
+ return;
+ }
+ i = (i + 1) & _table->sizeMask;
+ }
+
+ // Create a new hash table entry.
+ rep->ref();
+ _table->entries[i].key = rep;
+ _table->entries[i].value = value;
+ _table->entries[i].attributes = attributes;
+ ++_table->keyCount;
+
+ // store insert order
+ _table->indexCount++;
+ _table->hashToIndex[i] = _table->indexCount;
+
+#if DO_CONSISTENCY_CHECK// speed, why call a stub if we dont need to??
+ checkConsistency();
+#endif
+}
+
+inline void PropertyMap::insert(UString::Rep *key, ValueImp *value, int attributes)
+{
+ assert(_table);
+
+ int i = hash(key);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[i].key && _table->entries[i].key != key;
+#endif
+ while (_table->entries[i].key)
+ i = (i + 1) & _table->sizeMask;
+
+ _table->entries[i].key = key;
+ _table->entries[i].value = value;
+ _table->entries[i].attributes = attributes;
+}
+
+void PropertyMap::expand()
+{
+#if DO_CONSISTENCY_CHECK// speed, why call a stub if we dont need to??
+ checkConsistency();
+#endif
+ Table *oldTable = _table;
+
+ int oldTableSize = oldTable ? oldTable->size : 0;
+
+ int newTableSize = oldTableSize ? oldTableSize * 2 : 16;
+
+ // Is this realy the best way? wouldnt it be easier to use new/delete on an array instead
+ // and do a pointer in Table to that array, that way we wouldnt need to delete the whole _table
+ // every time we need to expand
+ _table = (Table *)calloc(1, sizeof(Table) + ((newTableSize - 1) * sizeof(Entry)) );
+
+ int *p = new int[newTableSize];
+ for (int i = 0; i < newTableSize; i++)
+ p[i] = 0;
+
+ _table->hashToIndex = p;
+
+ _table->size = newTableSize;
+
+ _table->sizeMask = newTableSize - 1;
+
+ _table->keyCount = oldTable ? oldTable->keyCount : 0;
+
+ _table->indexCount = oldTable ? oldTable->indexCount : 0;
+
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (key) {
+ insert(key, _singleEntry.value, _singleEntry.attributes);
+ _table->keyCount++;
+ _singleEntry.key = 0;
+
+ // store sort order
+ // first get the id of newly inserted key, check for trashed hash, then store it
+ int k = hash(key);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[k].key && _table->entries[k].key != key;
+#endif
+ while (UString::Rep *newKey = _table->entries[k].key) {
+ if (key == newKey)
+ break;
+ k = (k + 1) & _table->sizeMask;
+ }
+ _table->indexCount++;
+ _table->hashToIndex[k] = _table->indexCount;
+ }
+#endif
+
+ for (int i = 0; i != oldTableSize; ++i) {
+ UString::Rep *key = oldTable->entries[i].key;
+ if (key) {
+ insert(key, oldTable->entries[i].value, oldTable->entries[i].attributes);
+
+ // store sort order
+ // first get the id of newly inserted key, check for trashed hash, then store it
+ int k = hash(key);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[k].key && _table->entries[k].key != key;
+#endif
+ while (UString::Rep *newKey = _table->entries[k].key) {
+ if (key == newKey)
+ break;
+ k = (k + 1) & _table->sizeMask;
+ }
+ // store hashindex on the newly inserted entry
+ _table->hashToIndex[k] = oldTable->hashToIndex[i];
+ }
+ }
+
+ if (oldTable){
+ delete [] oldTable->hashToIndex;
+ }
+ free(oldTable);
+
+#if DO_CONSISTENCY_CHECK// speed, why call a stub if we dont need to??
+ checkConsistency();
+#endif
+}
+
+void PropertyMap::remove(const Identifier &name)
+{
+ assert(!name.isNull());
+
+#if DO_CONSISTENCY_CHECK// speed, why call a stub if we dont need to??
+ checkConsistency();
+#endif
+
+ UString::Rep *rep = name._ustring.rep;
+
+ UString::Rep *key;
+
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ key = _singleEntry.key;
+ if (rep == key) {
+ key->deref();
+ _singleEntry.key = 0;
+ checkConsistency();
+ }
+#endif
+ return;
+ }
+
+ // Find the thing to remove.
+ int i = hash(rep);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[i].key && _table->entries[i].key != rep;
+#endif
+ while ((key = _table->entries[i].key)) {
+ if (rep == key)
+ break;
+ i = (i + 1) & _table->sizeMask;
+ }
+
+ if (!key)
+ return;
+
+ // Remove the one key.
+ key->deref();
+ _table->entries[i].key = 0;
+ assert(_table->keyCount >= 1);
+ --_table->keyCount;
+
+ // Reinsert all the items to the right in the same cluster.
+ while (1) {
+ i = (i + 1) & _table->sizeMask;
+ key = _table->entries[i].key;
+ if (!key)
+ break;
+
+ _table->entries[i].key = 0;
+
+ insert(key, _table->entries[i].value, _table->entries[i].attributes);
+
+ // store the index of the new hash
+ int k = hash(key);
+#if DUMP_STATISTICS
+ ++numProbes;
+ numCollisions += _table->entries[k].key && _table->entries[k].key != key;
+#endif
+ while (UString::Rep *newKey = _table->entries[k].key) {
+ if (key == newKey)
+ break;
+ k = (k + 1) & _table->sizeMask;
+ }
+
+ // store hashindex on the newly moved entry
+ _table->hashToIndex[k] = _table->hashToIndex[i];
+ }
+
+#if DO_CONSISTENCY_CHECK// speed, why call a stub if we dont need to??
+ checkConsistency();
+#endif
+}
+
+void PropertyMap::mark() const
+{
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ if (_singleEntry.key) {
+ ValueImp *v = _singleEntry.value;
+ if (!v->marked())
+ v->mark();
+ }
+#endif
+ return;
+ }
+
+ for (int i = 0; i != _table->size; ++i) {
+ if (_table->entries[i].key) {
+ ValueImp *v = _table->entries[i].value;
+ if (!v->marked())
+ v->mark();
+ }
+ }
+}
+
+void PropertyMap::addEnumerablesToReferenceList(ReferenceList &list, const Object &base) const
+{
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (key && !(_singleEntry.attributes & DontEnum))
+ list.append(Reference(base, Identifier(key)));
+#endif
+ return;
+ }
+
+ // Allocate a buffer to use to sort the keys.
+ int indexSize = _table->indexCount + 1; // indexes is one based
+ UString::Rep **allKeys = new UString::Rep*[indexSize];
+
+ for (int i = 0; i < indexSize; i++)
+ allKeys[i] = NULL;
+
+ // push valid hashes to the array allKeys, using insert order as index.
+ // we need to pass array hashes instead of pointers to keys, because we got
+ // memory corruption sometimes, seems that Identifier in below call deletes the key
+ int size = _table->size;
+ Entry *entries = _table->entries;
+ for (int i = 0; i != size; ++i) {
+ if (entries[i].key && !(entries[i].attributes & DontEnum)) {
+ int idx = _table->hashToIndex[i];
+ if (idx) {
+ allKeys[idx] = entries[i].key;
+ } else { // nonsorted key, failure
+ //cout<<"Error with in KJS property_map.addEnumerablesToReferenceList \nUnsorted key"<<endl;
+ assert(0==1); // allways throw error if get here
+ }
+ }
+ }
+ // Put the keys of the sorted entries into the reference list.
+ for (int i = 0; i < indexSize; ++i) {
+ if (allKeys[i] != NULL){
+ list.append(Reference(base, Identifier(allKeys[i])));
+ }
+ allKeys[i] = NULL; // dont deallocate key by accident, when we delete allKeys
+ }
+
+ // Deallocate the buffer.
+ delete [] allKeys;
+}
+
+void PropertyMap::addSparseArrayPropertiesToReferenceList(ReferenceList &list, const Object &base) const
+{
+ // NOTE: I did'nt add sort in this method because It seems to be referenced in ArrayInstanceImp
+ // only and arrays are sorted by definition, dont need the extra overhead
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ UString::Rep *key = _singleEntry.key;
+ if (key) {
+ UString k(key);
+ bool fitsInULong;
+ unsigned long i = k.toULong(&fitsInULong);
+ if (fitsInULong && i <= 0xFFFFFFFFU)
+ list.append(Reference(base, Identifier(key)));
+ }
+#endif
+ return;
+ }
+
+ for (int i = 0; i != _table->size; ++i) {
+ UString::Rep *key = _table->entries[i].key;
+ if (key) {
+ UString k(key);
+ bool fitsInULong;
+ unsigned long i = k.toULong(&fitsInULong);
+ if (fitsInULong && i <= 0xFFFFFFFFU)
+ list.append(Reference(base, Identifier(key)));
+ }
+ }
+}
+
+void PropertyMap::save(SavedProperties &p) const
+{
+ int count = 0;
+
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ if (_singleEntry.key && !(_singleEntry.attributes & (ReadOnly | DontEnum | Function)))
+ ++count;
+#endif
+ } else {
+ for (int i = 0; i != _table->size; ++i)
+ if (_table->entries[i].key && !(_table->entries[i].attributes & (ReadOnly | DontEnum | Function)))
+ ++count;
+ }
+
+ delete [] p._properties;
+
+ p._count = count;
+
+ if (count == 0) {
+ p._properties = 0;
+ return;
+ }
+
+ p._properties = new SavedProperty [count];
+
+ SavedProperty *prop = p._properties;
+
+ if (!_table) {
+#if USE_SINGLE_ENTRY
+ if (_singleEntry.key && !(_singleEntry.attributes & (ReadOnly | DontEnum | Function))) {
+ prop->key = Identifier(_singleEntry.key);
+ prop->value = Value(_singleEntry.value);
+ prop->attributes = _singleEntry.attributes;
+ ++prop;
+ }
+#endif
+ } else {
+ for (int i = 0; i != _table->size; ++i) {
+ if (_table->entries[i].key && !(_table->entries[i].attributes & (ReadOnly | DontEnum | Function))) {
+ prop->key = Identifier(_table->entries[i].key);
+ prop->value = Value(_table->entries[i].value);
+ prop->attributes = _table->entries[i].attributes;
+ ++prop;
+ }
+ }
+ }
+}
+
+void PropertyMap::restore(const SavedProperties &p)
+{
+ for (int i = 0; i != p._count; ++i)
+ put(p._properties[i].key, p._properties[i].value.imp(), p._properties[i].attributes);
+}
+
+#if DO_CONSISTENCY_CHECK
+
+void PropertyMap::checkConsistency()
+{
+ if (!_table)
+ return;
+
+ int count = 0;
+ for (int j = 0; j != _table->size; ++j) {
+ UString::Rep *rep = _table->entries[j].key;
+ if (!rep)
+ continue;
+ int i = hash(rep);
+ while (UString::Rep *key = _table->entries[i].key) {
+ if (rep == key)
+ break;
+ i = (i + 1) & _table->sizeMask;
+ }
+ assert(i == j);
+ assert(_table->hashToIndex[i] > 0);
+ count++;
+ }
+ assert(count == _table->keyCount);
+ assert(_table->size >= 16);
+ assert(_table->sizeMask);
+ assert(_table->size == _table->sizeMask + 1);
+}
+
+#endif // DO_CONSISTENCY_CHECK
+
+} // namespace KJS
diff --git a/kjs/property_map.h b/kjs/property_map.h
new file mode 100644
index 000000000..4b1398e7b
--- /dev/null
+++ b/kjs/property_map.h
@@ -0,0 +1,107 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_PROPERTY_MAP_H_
+#define _KJS_PROPERTY_MAP_H_
+
+#include "identifier.h"
+
+namespace KJS {
+
+ class Object;
+ class ReferenceList;
+ class ValueImp;
+
+ class SavedProperty;
+
+ struct PropertyMapHashTable;
+
+/**
+* Saved Properties
+*/
+ class SavedProperties {
+ friend class PropertyMap;
+ public:
+ SavedProperties();
+ ~SavedProperties();
+
+ private:
+ int _count;
+ SavedProperty *_properties;
+
+ SavedProperties(const SavedProperties&);
+ SavedProperties& operator=(const SavedProperties&);
+ };
+
+/**
+* A hashtable entry for the @ref PropertyMap.
+*/
+ struct PropertyMapHashTableEntry
+ {
+ PropertyMapHashTableEntry() : key(0) { }
+ UString::Rep *key;
+ ValueImp *value;
+ int attributes;
+ };
+/**
+* Javascript Property Map.
+*/
+
+ class KJS_EXPORT PropertyMap {
+ public:
+ PropertyMap();
+ ~PropertyMap();
+
+ void clear();
+
+ void put(const Identifier &name, ValueImp *value, int attributes);
+ void remove(const Identifier &name);
+ ValueImp *get(const Identifier &name) const;
+ ValueImp *get(const Identifier &name, int &attributes) const;
+
+ void mark() const;
+ void addEnumerablesToReferenceList(ReferenceList &, const Object &) const;
+ void addSparseArrayPropertiesToReferenceList(ReferenceList &, const Object &) const;
+
+ void save(SavedProperties &) const;
+ void restore(const SavedProperties &p);
+
+ private:
+ int hash(const UString::Rep *) const;
+ static bool keysMatch(const UString::Rep *, const UString::Rep *);
+ void expand();
+
+ void insert(UString::Rep *, ValueImp *value, int attributes);
+
+ void checkConsistency();
+
+ typedef PropertyMapHashTableEntry Entry;
+ typedef PropertyMapHashTable Table;
+
+ Table *_table;
+
+ Entry _singleEntry;
+ };
+
+} // namespace
+
+#endif // _KJS_PROPERTY_MAP_H_
diff --git a/kjs/reference.cpp b/kjs/reference.cpp
new file mode 100644
index 000000000..778db8a53
--- /dev/null
+++ b/kjs/reference.cpp
@@ -0,0 +1,193 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "reference.h"
+#include "internal.h"
+#include "context.h"
+
+#include <assert.h>
+
+using namespace KJS;
+
+// ------------------------------ Reference ------------------------------------
+
+Reference::Reference(const Object& b, const Identifier& p)
+ : base(b),
+ baseIsValue(false),
+ propertyNameIsNumber(false),
+ prop(p)
+{
+}
+
+Reference::Reference(const Object& b, unsigned p)
+ : base(b),
+ propertyNameAsNumber(p),
+ baseIsValue(false),
+ propertyNameIsNumber(true)
+{
+}
+
+Reference::Reference(ObjectImp *b, const Identifier& p)
+ : base(b),
+ baseIsValue(false),
+ propertyNameIsNumber(false),
+ prop(p)
+{
+}
+
+Reference::Reference(ObjectImp *b, unsigned p)
+ : base(b),
+ propertyNameAsNumber(p),
+ baseIsValue(false),
+ propertyNameIsNumber(true)
+{
+}
+
+Reference::Reference(const Null& b, const Identifier& p)
+ : base(b),
+ baseIsValue(false),
+ propertyNameIsNumber(false),
+ prop(p)
+{
+}
+
+Reference::Reference(const Null& b, unsigned p)
+ : base(b),
+ propertyNameAsNumber(p),
+ baseIsValue(false),
+ propertyNameIsNumber(true)
+{
+}
+
+Reference Reference::makeValueReference(const Value& v)
+{
+ Reference valueRef;
+ valueRef.base = v;
+ valueRef.baseIsValue = true;
+ return valueRef;
+}
+
+Reference::Reference()
+{
+}
+
+Value Reference::getBase(ExecState *exec) const
+{
+ if (baseIsValue) {
+ Object err = Error::create(exec, ReferenceError, I18N_NOOP("Invalid reference base"));
+ exec->setException(err);
+ return err;
+ }
+
+ return base;
+}
+
+Identifier Reference::getPropertyName(ExecState * /*exec*/) const
+{
+ if (baseIsValue) {
+ // the spec wants a runtime error here. But getValue() and putValue()
+ // will catch this case on their own earlier. When returning a Null
+ // string we should be on the safe side.
+ return Identifier();
+ }
+
+ if (propertyNameIsNumber && prop.isNull())
+ prop = Identifier::from(propertyNameAsNumber);
+ return prop;
+}
+
+Value Reference::getValue(ExecState *exec) const
+{
+ if (baseIsValue) {
+ return base;
+ }
+
+ Value o = getBase(exec);
+
+ if (!o.isValid() || o.type() == NullType) {
+ UString m = I18N_NOOP("Can't find variable: ") + getPropertyName(exec).ustring();
+ Object err = Error::create(exec, ReferenceError, m.ascii());
+ exec->setException(err);
+ return err;
+ }
+
+ if (o.type() != ObjectType) {
+ UString m = I18N_NOOP("Base is not an object");
+ Object err = Error::create(exec, ReferenceError, m.ascii());
+ exec->setException(err);
+ return err;
+ }
+
+ ObjectImp *oimp = static_cast<ObjectImp*>(o.imp());
+ if (propertyNameIsNumber)
+ return oimp->getPropertyByIndex(exec, propertyNameAsNumber);
+ return oimp->get(exec, prop);
+}
+
+void Reference::putValue(ExecState *exec, const Value &w)
+{
+ if (baseIsValue) {
+ Object err = Error::create(exec,ReferenceError);
+ exec->setException(err);
+ return;
+ }
+
+#ifdef KJS_VERBOSE
+ printInfo(exec,(UString("setting property ")+getPropertyName(exec).ustring()).cstring().c_str(),w);
+#endif
+ Value o = getBase(exec);
+ if (o.type() == NullType)
+ o = Value(exec->context().imp()->scopeChain().bottom());
+
+ ObjectImp *oimp = static_cast<ObjectImp*>(o.imp());
+ if (propertyNameIsNumber)
+ oimp->putPropertyByIndex(exec, propertyNameAsNumber, w);
+ else
+ oimp->put(exec, prop, w);
+}
+
+bool Reference::deleteValue(ExecState *exec)
+{
+ if (baseIsValue) {
+ Object err = Error::create(exec,ReferenceError);
+ exec->setException(err);
+ return false;
+ }
+
+ Value b = getBase(exec);
+
+ // The spec doesn't mention what to do if the base is null... just return true
+ if (b.type() != ObjectType) {
+ assert(b.type() == NullType);
+ return true;
+ }
+
+ ObjectImp *bimp = static_cast<ObjectImp*>(b.imp());
+ if (propertyNameIsNumber)
+ return bimp->deletePropertyByIndex(exec, propertyNameAsNumber);
+ return bimp->deleteProperty(exec, prop);
+}
+
+bool Reference::isMutable()
+{
+ return !baseIsValue;
+}
diff --git a/kjs/reference.h b/kjs/reference.h
new file mode 100644
index 000000000..eafa39848
--- /dev/null
+++ b/kjs/reference.h
@@ -0,0 +1,89 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_REFERENCE_H_
+#define _KJS_REFERENCE_H_
+
+#include "identifier.h"
+#include "value.h"
+
+namespace KJS {
+
+// delme
+/**
+* Defines a Javascript reference.
+*/
+ class KJS_EXPORT Reference : public Value {
+// fixme
+/* class Reference : private Value { */
+ friend class ReferenceList;
+ friend class ReferenceListIterator;
+ public:
+ Reference(const Object& b, const Identifier& p);
+ Reference(const Object& b, unsigned p);
+ Reference(ObjectImp *b, const Identifier& p);
+ Reference(ObjectImp *b, unsigned p);
+ Reference(const Null& b, const Identifier& p);
+ Reference(const Null& b, unsigned p);
+ static Reference makeValueReference(const Value& v);
+
+ /**
+ * Performs the GetBase type conversion operation on this value (ECMA 8.7)
+ *
+ * Since references are supposed to have an Object or null as their base,
+ * this method is guaranteed to return either Null() or an Object value.
+ */
+ Value getBase(ExecState *exec) const;
+
+ /**
+ * Performs the GetPropertyName type conversion operation on this value
+ * (ECMA 8.7)
+ */
+ Identifier getPropertyName(ExecState *exec) const;
+
+ /**
+ * Performs the GetValue type conversion operation on this value
+ * (ECMA 8.7.1)
+ */
+ Value getValue(ExecState *exec) const;
+
+ /**
+ * Performs the PutValue type conversion operation on this value
+ * (ECMA 8.7.1)
+ */
+ void putValue(ExecState *exec, const Value &w);
+ bool deleteValue(ExecState *exec);
+
+ bool isMutable();
+
+ private:
+ Reference();
+
+ Value base;
+ unsigned propertyNameAsNumber;
+ bool baseIsValue;
+ bool propertyNameIsNumber;
+ mutable Identifier prop;
+ };
+}
+
+#endif
diff --git a/kjs/reference_list.cpp b/kjs/reference_list.cpp
new file mode 100644
index 000000000..d60fbd260
--- /dev/null
+++ b/kjs/reference_list.cpp
@@ -0,0 +1,154 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "reference_list.h"
+
+namespace KJS {
+ class ReferenceListNode {
+ friend class ReferenceList;
+ friend class ReferenceListIterator;
+
+ protected:
+ ReferenceListNode(const Reference &ref) : reference(ref), next(NULL) {}
+
+ private:
+ Reference reference;
+ ReferenceListNode *next;
+ };
+
+ class ReferenceListHeadNode : private ReferenceListNode {
+ friend class ReferenceList;
+ friend class ReferenceListIterator;
+
+ ReferenceListHeadNode(const Reference &ref) : ReferenceListNode(ref), refcount(1), length(0) {}
+ int refcount;
+ int length;
+ };
+
+}
+
+using namespace KJS;
+
+// ReferenceList
+
+ReferenceList::ReferenceList() :
+ head(NULL),
+ tail(NULL)
+{
+}
+
+ReferenceList::ReferenceList(const ReferenceList &list)
+{
+ head = list.head;
+ tail = list.tail;
+ if (head != NULL) {
+ head->refcount++;
+ }
+}
+
+ReferenceList &ReferenceList::operator=(const ReferenceList &list)
+{
+ ReferenceList tmp(list);
+ tmp.swap(*this);
+
+ return *this;
+}
+
+void ReferenceList::swap(ReferenceList &list)
+{
+ ReferenceListHeadNode *tmpHead = list.head;
+ list.head = head;
+ head = tmpHead;
+
+ ReferenceListNode *tmpTail = list.tail;
+ list.tail = tail;
+ tail = tmpTail;
+}
+
+
+void ReferenceList::append(const Reference& ref)
+{
+ if (tail == NULL) {
+ tail = head = new ReferenceListHeadNode(ref);
+ } else {
+ tail->next = new ReferenceListNode(ref);
+ tail = tail->next;
+ }
+ head->length++;
+}
+
+int ReferenceList::length()
+{
+ return head ? head->length : 0;
+}
+
+ReferenceList::~ReferenceList()
+{
+ if (head != NULL && --(head->refcount) == 0) {
+ ReferenceListNode *next;
+
+ for (ReferenceListNode *p = head; p != NULL; p = next) {
+ next = p->next;
+ if (p == head) {
+ delete (ReferenceListHeadNode *)p;
+ } else {
+ delete p;
+ }
+ }
+ }
+}
+
+ReferenceListIterator ReferenceList::begin() const
+{
+ return ReferenceListIterator(head);
+}
+
+ReferenceListIterator ReferenceList::end() const
+{
+ return ReferenceListIterator(NULL);
+}
+
+
+// ReferenceListIterator
+
+
+ReferenceListIterator::ReferenceListIterator(ReferenceListNode *n) :
+ node(n)
+{
+}
+
+bool ReferenceListIterator::operator!=(const ReferenceListIterator &it) const
+{
+ return node != it.node;
+}
+
+const Reference *ReferenceListIterator::operator->() const
+{
+ return &node->reference;
+}
+
+const Reference &ReferenceListIterator::operator++(int /*i*/)
+{
+ const Reference &ref = node->reference;
+ node = node->next;
+ return ref;
+}
diff --git a/kjs/reference_list.h b/kjs/reference_list.h
new file mode 100644
index 000000000..20293a736
--- /dev/null
+++ b/kjs/reference_list.h
@@ -0,0 +1,75 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_REFERENCE_LIST_H_
+#define _KJS_REFERENCE_LIST_H_
+
+#include "types.h"
+#include "reference.h"
+
+namespace KJS {
+
+ class ReferenceListNode;
+ class ReferenceListHeadNode;
+
+/**
+* An iterator for a @ref ReferenceList.
+*/
+ class KJS_EXPORT ReferenceListIterator {
+ friend class ReferenceList;
+
+ public:
+ bool operator!=(const ReferenceListIterator &it) const;
+ const Reference *operator->() const;
+ const Reference &operator++(int i);
+
+ private:
+ ReferenceListIterator(ReferenceListNode *n);
+ ReferenceListIterator();
+ ReferenceListNode *node;
+ };
+
+/**
+* A list of @ref Reference objects.
+*/
+ class KJS_EXPORT ReferenceList {
+ public:
+ ReferenceList();
+ ReferenceList(const ReferenceList &list);
+ ReferenceList &operator=(const ReferenceList &list);
+ ~ReferenceList();
+
+ void append(const Reference& val);
+ int length();
+
+ ReferenceListIterator begin() const;
+ ReferenceListIterator end() const;
+
+ private:
+ void swap(ReferenceList &list);
+ ReferenceListHeadNode *head;
+ ReferenceListNode *tail;
+ };
+
+}
+
+#endif
diff --git a/kjs/regexp.cpp b/kjs/regexp.cpp
new file mode 100644
index 000000000..06defcc53
--- /dev/null
+++ b/kjs/regexp.cpp
@@ -0,0 +1,443 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2003,2004 Apple Computer, Inc.
+ * Copyright (C) 2006 Maksim Orlovich (maksim@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
+ *
+ */
+
+#include "regexp.h"
+
+#include "lexer.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace KJS;
+
+#ifdef PCRE_CONFIG_UTF8
+RegExp::UTF8SupportState RegExp::utf8Support = RegExp::Unknown;
+#endif
+
+RegExp::RegExp(const UString &p, int f)
+ : pat(p), flgs(f), m_notEmpty(false), valid(true), buffer(0), originalPos(0)
+{
+ // Determine whether libpcre has unicode support if need be..
+#ifdef PCRE_CONFIG_UTF8
+ if (utf8Support == Unknown) {
+ int supported;
+ pcre_config(PCRE_CONFIG_UTF8, (void*)&supported);
+ utf8Support = supported ? Supported : Unsupported;
+ }
+#endif
+
+ nrSubPatterns = 0; // determined in match() with POSIX regex.
+
+ // JS regexps can contain Unicode escape sequences (\uxxxx) which
+ // are rather uncommon elsewhere. As our regexp libs don't understand
+ // them we do the unescaping ourselves internally.
+ // Also make sure to expand out any nulls as pcre_compile
+ // expects null termination..
+ UString intern;
+ const char* const nil = "\\x00";
+ if (p.find('\\') >= 0 || p.find(KJS::UChar('\0')) >= 0) {
+ bool escape = false;
+ for (int i = 0; i < p.size(); ++i) {
+ UChar c = p[i];
+ if (escape) {
+ escape = false;
+ // we only care about \u
+ if (c == 'u') {
+ // standard unicode escape sequence looks like \uxxxx but
+ // other browsers also accept less then 4 hex digits
+ unsigned short u = 0;
+ int j = 0;
+ for (j = 0; j < 4; ++j) {
+ if (i + 1 < p.size() && Lexer::isHexDigit(p[i + 1].unicode())) {
+ u = (u << 4) + Lexer::convertHex(p[i + 1].unicode());
+ ++i;
+ } else {
+ // sequence incomplete. restore index.
+ // TODO: cleaner way to propagate warning
+ fprintf(stderr, "KJS: saw %d digit \\u sequence.\n", j);
+ i -= j;
+ break;
+ }
+ }
+ if (j < 4) {
+ // sequence was incomplete. treat \u as u which IE always
+ // and FF sometimes does.
+ intern.append(UString('u'));
+ } else {
+ c = UChar(u);
+ switch (u) {
+ case 0:
+ // Make sure to encode 0, to avoid terminating the string
+ intern += UString(nil);
+ break;
+ case '^':
+ case '$':
+ case '\\':
+ case '.':
+ case '*':
+ case '+':
+ case '?':
+ case '(': case ')':
+ case '{': case '}':
+ case '[': case ']':
+ case '|':
+ // escape pattern characters have to remain escaped
+ intern.append(UString('\\'));
+ // intentional fallthrough
+ default:
+ intern += UString(&c, 1);
+ break;
+ }
+ }
+ continue;
+ }
+ intern += UString('\\');
+ intern += UString(&c, 1);
+ } else {
+ if (c == '\\')
+ escape = true;
+ else if (c == '\0')
+ intern += UString(nil);
+ else
+ intern += UString(&c, 1);
+ }
+ }
+ } else {
+ intern = p;
+ }
+
+#ifdef HAVE_PCREPOSIX
+ int pcreflags = 0;
+ const char *perrormsg;
+ int errorOffset;
+
+ if (flgs & IgnoreCase)
+ pcreflags |= PCRE_CASELESS;
+
+ if (flgs & Multiline)
+ pcreflags |= PCRE_MULTILINE;
+
+#ifdef PCRE_CONFIG_UTF8
+ if (utf8Support == Supported)
+ pcreflags |= (PCRE_UTF8 | PCRE_NO_UTF8_CHECK);
+#endif
+
+ // Fill our buffer with an encoded version, whether utf-8, or,
+ // if PCRE is incapable, truncated.
+ prepareMatch(intern);
+
+ pcregex = pcre_compile(buffer, pcreflags,
+ &perrormsg, &errorOffset, NULL);
+ doneMatch(); // Cleanup buffers
+ if (!pcregex) {
+#ifndef NDEBUG
+ fprintf(stderr, "KJS: pcre_compile() failed with '%s'\n", perrormsg);
+#endif
+ valid = false;
+ return;
+ }
+
+#ifdef PCRE_INFO_CAPTURECOUNT
+ // Get number of subpatterns that will be returned
+ int rc = pcre_fullinfo( pcregex, NULL, PCRE_INFO_CAPTURECOUNT, &nrSubPatterns);
+ if (rc != 0)
+#endif
+ nrSubPatterns = 0; // fallback. We always need the first pair of offsets.
+
+#else /* HAVE_PCREPOSIX */
+
+ int regflags = 0;
+#ifdef REG_EXTENDED
+ regflags |= REG_EXTENDED;
+#endif
+#ifdef REG_ICASE
+ if ( f & IgnoreCase )
+ regflags |= REG_ICASE;
+#endif
+
+ //NOTE: Multiline is not feasible with POSIX regex.
+ //if ( f & Multiline )
+ // ;
+ // Note: the Global flag is already handled by RegExpProtoFunc::execute
+
+ int errorCode = regcomp(&preg, intern.ascii(), regflags);
+ if (errorCode != 0) {
+#ifndef NDEBUG
+ char errorMessage[80];
+ regerror(errorCode, &preg, errorMessage, sizeof errorMessage);
+ fprintf(stderr, "KJS: regcomp failed with '%s'\n", errorMessage);
+#endif
+ valid = false;
+ }
+#endif
+}
+
+RegExp::~RegExp()
+{
+ doneMatch(); // Be 100% sure buffers are freed
+#ifdef HAVE_PCREPOSIX
+ if (pcregex)
+ pcre_free(pcregex);
+#else
+ /* TODO: is this really okay after an error ? */
+ regfree(&preg);
+#endif
+}
+
+void RegExp::prepareUtf8(const UString& s)
+{
+ // Allocate a buffer big enough to hold all the characters plus \0
+ const int length = s.size();
+ buffer = new char[length * 3 + 1];
+
+ // Also create buffer for positions. We need one extra character in there,
+ // even past the \0 since the non-empty handling may jump one past the end
+ originalPos = new int[length * 3 + 2];
+
+ // Convert to runs of 8-bit characters, and generate indeces
+ // Note that we do NOT combine surrogate pairs here, as
+ // regexps operate on them as separate characters
+ char *p = buffer;
+ int *posOut = originalPos;
+ const UChar *d = s.data();
+ for (int i = 0; i != length; ++i) {
+ unsigned short c = d[i].unicode();
+
+ int sequenceLen;
+ if (c < 0x80) {
+ *p++ = (char)c;
+ sequenceLen = 1;
+ } else if (c < 0x800) {
+ *p++ = (char)((c >> 6) | 0xC0); // C0 is the 2-byte flag for UTF-8
+ *p++ = (char)((c | 0x80) & 0xBF); // next 6 bits, with high bit set
+ sequenceLen = 2;
+ } else {
+ *p++ = (char)((c >> 12) | 0xE0); // E0 is the 3-byte flag for UTF-8
+ *p++ = (char)(((c >> 6) | 0x80) & 0xBF); // next 6 bits, with high bit set
+ *p++ = (char)((c | 0x80) & 0xBF); // next 6 bits, with high bit set
+ sequenceLen = 3;
+ }
+
+ while (sequenceLen > 0) {
+ *posOut = i;
+ ++posOut;
+ --sequenceLen;
+ }
+ }
+
+ bufferSize = p - buffer;
+
+ *p++ = '\0';
+
+ // Record positions for \0, and the fictional character after that.
+ *posOut = length;
+ *(posOut+1) = length+1;
+}
+
+void RegExp::prepareASCII (const UString& s)
+{
+ originalPos = 0;
+
+ // Best-effort attempt to get something done
+ // when we don't have utf 8 available -- use
+ // truncated version, and pray for the best
+ CString truncated = s.cstring();
+ buffer = new char[truncated.size() + 1];
+ memcpy(buffer, truncated.c_str(), truncated.size());
+ buffer[truncated.size()] = '\0'; // For _compile use
+ bufferSize = truncated.size();
+}
+
+void RegExp::prepareMatch(const UString &s)
+{
+ delete[] originalPos; // Just to be sure..
+ delete[] buffer;
+#ifdef PCRE_CONFIG_UTF8
+ if (utf8Support == Supported)
+ prepareUtf8(s);
+ else
+#endif
+ prepareASCII(s);
+
+#ifndef NDEBUG
+ originalS = s;
+#endif
+}
+
+void RegExp::doneMatch()
+{
+ delete[] originalPos; originalPos = 0;
+ delete[] buffer; buffer = 0;
+}
+
+UString RegExp::match(const UString &s, int i, int *pos, int **ovector)
+{
+#ifndef NDEBUG
+ assert(s.data() == originalS.data()); // Make sure prepareMatch got called right..
+#endif
+ assert(valid);
+
+ if (i < 0)
+ i = 0;
+ if (ovector)
+ *ovector = 0L;
+ int dummyPos;
+ if (!pos)
+ pos = &dummyPos;
+ *pos = -1;
+ if (i > s.size() || s.isNull())
+ return UString::null;
+
+#ifdef HAVE_PCREPOSIX
+ int ovecsize = (nrSubPatterns+1)*3; // see pcre docu
+ if (ovector) *ovector = new int[ovecsize];
+ if (!pcregex)
+ return UString::null;
+
+ int startPos;
+ int nextPos;
+
+#ifdef PCRE_CONFIG_UTF8
+ if (utf8Support == Supported) {
+ startPos = i;
+ while (originalPos[startPos] < i)
+ ++startPos;
+
+ nextPos = startPos;
+ while (originalPos[nextPos] < (i + 1))
+ ++nextPos;
+ } else
+#endif
+ {
+ startPos = i;
+ nextPos = i + 1;
+ }
+
+ int baseFlags =
+#ifdef PCRE_CONFIG_UTF8
+ utf8Support == Supported ? PCRE_NO_UTF8_CHECK :
+#endif
+ 0;
+ if (pcre_exec(pcregex, NULL, buffer, bufferSize, startPos,
+ m_notEmpty ? (PCRE_NOTEMPTY | PCRE_ANCHORED | baseFlags) : baseFlags, // see man pcretest
+ ovector ? *ovector : 0L, ovecsize) == PCRE_ERROR_NOMATCH)
+ {
+ // Failed to match.
+ if ((flgs & Global) && m_notEmpty && ovector)
+ {
+ // We set m_notEmpty ourselves, to look for a non-empty match
+ // (see man pcretest or pcretest.c for details).
+ // So we don't stop here, we want to try again at i+1.
+#ifdef KJS_VERBOSE
+ fprintf(stderr, "No match after m_notEmpty. +1 and keep going.\n");
+#endif
+ m_notEmpty = 0;
+ if (pcre_exec(pcregex, NULL, buffer, bufferSize, nextPos, baseFlags,
+ ovector ? *ovector : 0L, ovecsize) == PCRE_ERROR_NOMATCH)
+ return UString::null;
+ }
+ else // done
+ return UString::null;
+ }
+
+ // Got a match, proceed with it.
+ // But fix up the ovector if need be..
+ if (ovector && originalPos) {
+ for (unsigned c = 0; c < 2 * (nrSubPatterns + 1); ++c) {
+ if ((*ovector)[c] != -1)
+ (*ovector)[c] = originalPos[(*ovector)[c]];
+ }
+ }
+
+ if (!ovector)
+ return UString::null; // don't rely on the return value if you pass ovector==0
+#else
+ const uint maxMatch = 10;
+ regmatch_t rmatch[maxMatch];
+
+ char *str = strdup(s.ascii()); // TODO: why ???
+ if (regexec(&preg, str + i, maxMatch, rmatch, 0)) {
+ free(str);
+ return UString::null;
+ }
+ free(str);
+
+ if (!ovector) {
+ *pos = rmatch[0].rm_so + i;
+ return s.substr(rmatch[0].rm_so + i, rmatch[0].rm_eo - rmatch[0].rm_so);
+ }
+
+ // map rmatch array to ovector used in PCRE case
+ nrSubPatterns = 0;
+ for (uint j = 0; j < maxMatch && rmatch[j].rm_so >= 0 ; j++) {
+ nrSubPatterns++;
+ // if the nonEmpty flag is set, return a failed match if any of the
+ // subMatches happens to be an empty string.
+ if (m_notEmpty && rmatch[j].rm_so == rmatch[j].rm_eo)
+ return UString::null;
+ }
+ // Allow an ovector slot to return the (failed) match result.
+ if (nrSubPatterns == 0) nrSubPatterns = 1;
+
+ int ovecsize = (nrSubPatterns)*3; // see above
+ *ovector = new int[ovecsize];
+ for (uint j = 0; j < nrSubPatterns; j++) {
+ (*ovector)[2*j] = rmatch[j].rm_so + i;
+ (*ovector)[2*j+1] = rmatch[j].rm_eo + i;
+ }
+#endif
+
+ *pos = (*ovector)[0];
+ if ( *pos == (*ovector)[1] && (flgs & Global) )
+ {
+ // empty match, next try will be with m_notEmpty=true
+ m_notEmpty=true;
+ }
+ return s.substr((*ovector)[0], (*ovector)[1] - (*ovector)[0]);
+}
+
+#if 0 // unused
+bool RegExp::test(const UString &s, int)
+{
+#ifdef HAVE_PCREPOSIX
+ int ovector[300];
+ CString buffer(s.cstring());
+
+ if (s.isNull() ||
+ pcre_exec(pcregex, NULL, buffer.c_str(), buffer.size(), 0,
+ 0, ovector, 300) == PCRE_ERROR_NOMATCH)
+ return false;
+ else
+ return true;
+
+#else
+
+ char *str = strdup(s.ascii());
+ int r = regexec(&preg, str, 0, 0, 0);
+ free(str);
+
+ return r == 0;
+#endif
+}
+#endif
diff --git a/kjs/regexp.h b/kjs/regexp.h
new file mode 100644
index 000000000..3a14728d0
--- /dev/null
+++ b/kjs/regexp.h
@@ -0,0 +1,97 @@
+// -*- 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 _KJS_REGEXP_H_
+#define _KJS_REGEXP_H_
+
+#include <sys/types.h>
+
+#include "config.h"
+
+#ifdef HAVE_PCREPOSIX
+#include <pcre.h>
+#else // POSIX regex - not so good...
+extern "C" { // bug with some libc5 distributions
+#include <regex.h>
+}
+#endif //HAVE_PCREPOSIX
+
+#include "ustring.h"
+
+namespace KJS {
+
+ class RegExp {
+ public:
+ enum { None = 0, Global = 1, IgnoreCase = 2, Multiline = 4 };
+ RegExp(const UString &p, int f = None);
+ ~RegExp();
+ int flags() const { return flgs; }
+ UString pattern() const { return pat; }
+ bool isValid() const { return valid; }
+ UString match(const UString &s, int i, int *pos = 0, int **ovector = 0);
+ // test is unused. The JS spec says that RegExp.test should use
+ // RegExp.exec, so it has to store $1 etc.
+ // bool test(const UString &s, int i = -1);
+ unsigned int subPatterns() const { return nrSubPatterns; }
+
+ //These methods should be called around the match of the same string..
+ void prepareMatch(const UString &s);
+ void doneMatch();
+ private:
+ const UString pat;
+ int flgs : 8;
+ bool m_notEmpty;
+ bool valid;
+
+ // Cached encoding info...
+ char* buffer;
+ int* originalPos;
+ int bufferSize;
+
+ void prepareUtf8 (const UString& s);
+ void prepareASCII (const UString& s);
+#ifndef NDEBUG
+ UString originalS; // the original string, used for sanity-checking
+#endif
+
+#ifndef HAVE_PCREPOSIX
+ regex_t preg;
+#else
+ pcre *pcregex;
+
+ enum UTF8SupportState {
+ Unknown,
+ Supported,
+ Unsupported
+ };
+
+#ifdef PCRE_CONFIG_UTF8
+ static UTF8SupportState utf8Support;
+#endif
+#endif
+ unsigned int nrSubPatterns;
+
+ RegExp();
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/regexp_object.cpp b/kjs/regexp_object.cpp
new file mode 100644
index 000000000..edd2bbeca
--- /dev/null
+++ b/kjs/regexp_object.cpp
@@ -0,0 +1,378 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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 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 <stdio.h>
+
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "internal.h"
+#include "regexp.h"
+#include "regexp_object.h"
+#include "error_object.h"
+#include "lookup.h"
+
+using namespace KJS;
+
+// ------------------------------ RegExpPrototypeImp ---------------------------
+
+// ECMA 15.9.4
+
+const ClassInfo RegExpPrototypeImp::info = {"RegExp", 0, 0, 0};
+
+RegExpPrototypeImp::RegExpPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto,
+ FunctionPrototypeImp *funcProto)
+ : ObjectImp(objProto)
+{
+ Value protect(this);
+ setInternalValue(String(""));
+
+ // The constructor will be added later in RegExpObject's constructor (?)
+
+ static const Identifier execPropertyName("exec");
+ putDirect(execPropertyName,
+ new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::Exec, 0, execPropertyName), DontEnum);
+ static const Identifier testPropertyName("test");
+ putDirect(testPropertyName,
+ new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::Test, 0, testPropertyName), DontEnum);
+ putDirect(toStringPropertyName,
+ new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::ToString, 0, toStringPropertyName), DontEnum);
+ static const Identifier compilePropertyName("compile");
+ putDirect(compilePropertyName,
+ new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::Compile, 1, compilePropertyName), DontEnum);
+}
+
+// ------------------------------ RegExpProtoFuncImp ---------------------------
+
+RegExpProtoFuncImp::RegExpProtoFuncImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
+ int i, int len, const Identifier &_ident)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+ ident = _ident;
+}
+
+bool RegExpProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value RegExpProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ if (!thisObj.inherits(&RegExpImp::info)) {
+ if (thisObj.inherits(&RegExpPrototypeImp::info)) {
+ switch (id) {
+ case ToString: return String("//"); // FireFox returns /(?:)/
+ }
+ }
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+ RegExpImp *reimp = static_cast<RegExpImp*>(thisObj.imp());
+ RegExp *re = reimp->regExp();
+ String s;
+ UString str;
+ switch (id) {
+ case Exec: // 15.10.6.2
+ case Test:
+ {
+ s = args[0].toString(exec);
+ int length = s.value().size();
+
+ // Get values from the last time (in case of /g)
+ Value lastIndex = thisObj.get(exec,"lastIndex");
+ int i = lastIndex.isValid() ? lastIndex.toInt32(exec) : 0;
+ bool globalFlag = thisObj.get(exec,"global").toBoolean(exec);
+ if (!globalFlag)
+ i = 0;
+ if (i < 0 || i > length) {
+ thisObj.put(exec,"lastIndex", Number(0), DontDelete | DontEnum);
+ if (id == Test)
+ return Boolean(false);
+ else
+ return Null();
+ }
+ RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp().imp());
+ int **ovector = regExpObj->registerRegexp( re, s.value() );
+
+ re->prepareMatch(s.value());
+ str = re->match(s.value(), i, 0L, ovector);
+ re->doneMatch();
+ regExpObj->setSubPatterns(re->subPatterns());
+
+ if (id == Test)
+ return Boolean(!str.isNull());
+
+ if (str.isNull()) // no match
+ {
+ if (globalFlag)
+ thisObj.put(exec,"lastIndex",Number(0), DontDelete | DontEnum);
+ return Null();
+ }
+ else // success
+ {
+ if (globalFlag)
+ thisObj.put(exec,"lastIndex",Number( (*ovector)[1] ), DontDelete | DontEnum);
+ return regExpObj->arrayOfMatches(exec,str);
+ }
+ }
+ break;
+ case ToString:
+ s = thisObj.get(exec,"source").toString(exec);
+ str = "/";
+ str += s.value();
+ str += "/";
+ if (thisObj.get(exec,"global").toBoolean(exec)) {
+ str += "g";
+ }
+ if (thisObj.get(exec,"ignoreCase").toBoolean(exec)) {
+ str += "i";
+ }
+ if (thisObj.get(exec,"multiline").toBoolean(exec)) {
+ str += "m";
+ }
+ return String(str);
+ case Compile: {
+ RegExp* newEngine = RegExpObjectImp::makeEngine(exec, args[0].toString(exec), args[1]);
+ if (!newEngine)
+ return exec->exception();
+ reimp->setRegExp(newEngine);
+ return Value(reimp);
+ }
+ }
+
+
+ return Undefined();
+}
+
+// ------------------------------ RegExpImp ------------------------------------
+
+const ClassInfo RegExpImp::info = {"RegExp", 0, 0, 0};
+
+RegExpImp::RegExpImp(RegExpPrototypeImp *regexpProto)
+ : ObjectImp(regexpProto), reg(0L)
+{
+}
+
+RegExpImp::~RegExpImp()
+{
+ delete reg;
+}
+
+void RegExpImp::setRegExp(RegExp *r)
+{
+ delete reg;
+ reg = r;
+
+ Object protect(this);//Protect self from GC (we are allocating a StringImp, and may be new)
+ putDirect("global", (r->flags() & RegExp::Global) ? BooleanImp::staticTrue : BooleanImp::staticFalse,
+ DontDelete | ReadOnly | DontEnum);
+ putDirect("ignoreCase", (r->flags() & RegExp::IgnoreCase) ? BooleanImp::staticTrue : BooleanImp::staticFalse,
+ DontDelete | ReadOnly | DontEnum);
+ putDirect("multiline", (r->flags() & RegExp::Multiline) ? BooleanImp::staticTrue : BooleanImp::staticFalse,
+ DontDelete | ReadOnly | DontEnum);
+
+ putDirect("source", new StringImp(r->pattern()), DontDelete | ReadOnly | DontEnum);
+ putDirect("lastIndex", NumberImp::zero(), DontDelete | DontEnum);
+}
+
+// ------------------------------ RegExpObjectImp ------------------------------
+
+RegExpObjectImp::RegExpObjectImp(ExecState * /*exec*/,
+ FunctionPrototypeImp *funcProto,
+ RegExpPrototypeImp *regProto)
+
+ : InternalFunctionImp(funcProto), lastOvector(0L), lastNrSubPatterns(0)
+{
+ Value protect(this);
+ // ECMA 15.10.5.1 RegExp.prototype
+ putDirect(prototypePropertyName, regProto, DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, NumberImp::two(), ReadOnly|DontDelete|DontEnum);
+}
+
+RegExpObjectImp::~RegExpObjectImp()
+{
+ delete [] lastOvector;
+}
+
+int **RegExpObjectImp::registerRegexp( const RegExp* re, const UString& s )
+{
+ lastString = s;
+ delete [] lastOvector;
+ lastOvector = 0;
+ lastNrSubPatterns = re->subPatterns();
+ return &lastOvector;
+}
+
+Object RegExpObjectImp::arrayOfMatches(ExecState *exec, const UString &result) const
+{
+ List list;
+ // The returned array contains 'result' as first item, followed by the list of matches
+ list.append(String(result));
+ if ( lastOvector )
+ for ( unsigned int i = 1 ; i < lastNrSubPatterns + 1 ; ++i )
+ {
+ UString substring = lastString.substr( lastOvector[2*i], lastOvector[2*i+1] - lastOvector[2*i] );
+ list.append(String(substring));
+ }
+ Object arr = exec->lexicalInterpreter()->builtinArray().construct(exec, list);
+ arr.put(exec, "index", Number(lastOvector[0]));
+ arr.put(exec, "input", String(lastString));
+ return arr;
+}
+
+Value RegExpObjectImp::get(ExecState *exec, const Identifier &p) const
+{
+ UString s = p.ustring();
+ if (s[0] == '$' && lastOvector)
+ {
+ bool ok;
+ unsigned long i = s.substr(1).toULong(&ok);
+ if (ok)
+ {
+ if (i < lastNrSubPatterns + 1)
+ {
+ UString substring = lastString.substr( lastOvector[2*i], lastOvector[2*i+1] - lastOvector[2*i] );
+ return String(substring);
+ }
+ return String("");
+ }
+ }
+ return InternalFunctionImp::get(exec, p);
+}
+
+bool RegExpObjectImp::hasProperty(ExecState *exec, const Identifier &p) const
+{
+ UString s = p.ustring();
+ if (s[0] == '$' && lastOvector) {
+ bool ok;
+ (void)s.substr(1).toULong(&ok);
+ if (ok)
+ return true;
+ }
+
+ return InternalFunctionImp::hasProperty(exec, p);
+}
+
+bool RegExpObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+RegExp* RegExpObjectImp::makeEngine(ExecState *exec, const UString &p, const Value &flagsInput)
+{
+ UString flags = flagsInput.type() == UndefinedType ? UString("") : flagsInput.toString(exec);
+
+ // Check for validity of flags
+ for (int pos = 0; pos < flags.size(); ++pos) {
+ switch (flags[pos].unicode()) {
+ case 'g':
+ case 'i':
+ case 'm':
+ break;
+ default: {
+ Object err = Error::create(exec, SyntaxError,
+ "Invalid regular expression flags");
+ exec->setException(err);
+ return 0;
+ }
+ }
+ }
+
+ bool global = (flags.find("g") >= 0);
+ bool ignoreCase = (flags.find("i") >= 0);
+ bool multiline = (flags.find("m") >= 0);
+
+ int reflags = RegExp::None;
+ if (global)
+ reflags |= RegExp::Global;
+ if (ignoreCase)
+ reflags |= RegExp::IgnoreCase;
+ if (multiline)
+ reflags |= RegExp::Multiline;
+
+ RegExp *re = new RegExp(p, reflags);
+ if (!re->isValid()) {
+ Object err = Error::create(exec, SyntaxError,
+ "Invalid regular expression");
+ exec->setException(err);
+ delete re;
+ return 0;
+ }
+ return re;
+}
+
+// ECMA 15.10.4
+Object RegExpObjectImp::construct(ExecState *exec, const List &args)
+{
+ UString p;
+ if (args.isEmpty()) {
+ p = "";
+ } else {
+ Value a0 = args[0];
+ if (a0.isA(ObjectType) && a0.toObject(exec).inherits(&RegExpImp::info)) {
+ // It's a regexp. Check that no flags were passed.
+ if (args.size() > 1 && args[1].type() != UndefinedType) {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+ RegExpImp *rimp = static_cast<RegExpImp*>(Object::dynamicCast(a0).imp());
+ p = rimp->regExp()->pattern();
+ } else {
+ p = a0.toString(exec);
+ }
+ }
+
+ RegExp* re = makeEngine(exec, p, args[1]);
+ if (!re)
+ return exec->exception().toObject(exec);
+
+ RegExpPrototypeImp *proto = static_cast<RegExpPrototypeImp*>(exec->lexicalInterpreter()->builtinRegExpPrototype().imp());
+ RegExpImp *dat = new RegExpImp(proto);
+ Object obj(dat); // protect from GC
+ dat->setRegExp(re);
+
+ return obj;
+}
+
+bool RegExpObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.10.3
+Value RegExpObjectImp::call(ExecState *exec, Object &/*thisObj*/,
+ const List &args)
+{
+ // TODO: handle RegExp argument case (15.10.3.1)
+
+ return construct(exec, args);
+}
diff --git a/kjs/regexp_object.h b/kjs/regexp_object.h
new file mode 100644
index 000000000..9f24ce7c9
--- /dev/null
+++ b/kjs/regexp_object.h
@@ -0,0 +1,97 @@
+// -*- 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 _REGEXP_OBJECT_H_
+#define _REGEXP_OBJECT_H_
+
+#include "internal.h"
+#include "function_object.h"
+#include "regexp.h"
+
+namespace KJS {
+ class ExecState;
+ class RegExpPrototypeImp : public ObjectImp {
+ public:
+ RegExpPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto,
+ FunctionPrototypeImp *funcProto);
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ };
+
+ class RegExpProtoFuncImp : public InternalFunctionImp {
+ public:
+ RegExpProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len,
+ const Identifier &_ident);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { Exec, Test, ToString, Compile };
+ private:
+ int id;
+ };
+
+ class RegExpImp : public ObjectImp {
+ public:
+ RegExpImp(RegExpPrototypeImp *regexpProto);
+ ~RegExpImp();
+ void setRegExp(RegExp *r);
+ RegExp* regExp() { return reg; }
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ private:
+ RegExp *reg;
+ };
+
+ class RegExpObjectImp : public InternalFunctionImp {
+ public:
+ RegExpObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ RegExpPrototypeImp *regProto);
+ virtual ~RegExpObjectImp();
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ Value get(ExecState *exec, const Identifier &p) const;
+ bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
+ int ** registerRegexp( const RegExp* re, const UString& s );
+ void setSubPatterns(int num) { lastNrSubPatterns = num; }
+ Object arrayOfMatches(ExecState *exec, const UString &result) const;
+
+ /*
+ Attempts to create a new regular expression engine for the string p
+ and the flags stored in flagsInput. If this succeeds, it returns the
+ engine. If not, it returns 0, and raises an exception in exec
+ */
+ static RegExp* makeEngine(ExecState *exec, const UString &p, const Value &flagsInput);
+ private:
+ UString lastString;
+ int *lastOvector;
+ unsigned int lastNrSubPatterns;
+ };
+
+} // namespace
+
+#endif
diff --git a/kjs/scope_chain.cpp b/kjs/scope_chain.cpp
new file mode 100644
index 000000000..76eefe973
--- /dev/null
+++ b/kjs/scope_chain.cpp
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scope_chain.h"
+
+#include "object.h"
+
+#include <assert.h>
+
+namespace KJS {
+
+inline void ScopeChain::ref() const
+{
+ for (ScopeChainNode *n = _node; n; n = n->next) {
+ if (n->refCount++ != 0)
+ break;
+ }
+}
+
+ScopeChain &ScopeChain::operator=(const ScopeChain &c)
+{
+ c.ref();
+ deref();
+ _node = c._node;
+ return *this;
+}
+
+void ScopeChain::push(ObjectImp *o)
+{
+ assert(o);
+ _node = new ScopeChainNode(_node, o);
+}
+
+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;
+ }
+}
+
+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);
+}
+
+void ScopeChain::mark()
+{
+ for (ScopeChainNode *n = _node; n; n = n->next) {
+ ObjectImp *o = n->object;
+ if (!o->marked())
+ o->mark();
+ }
+}
+
+} // namespace KJS
diff --git a/kjs/scope_chain.h b/kjs/scope_chain.h
new file mode 100644
index 000000000..d7ddfbf8f
--- /dev/null
+++ b/kjs/scope_chain.h
@@ -0,0 +1,79 @@
+/*
+ * 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 "global.h"
+
+namespace KJS {
+
+ class ObjectImp;
+
+/**
+* A scope chain node.
+*/
+ class KJS_EXPORT ScopeChainNode {
+ public:
+ ScopeChainNode(ScopeChainNode *n, ObjectImp *o)
+ : next(n), object(o), refCount(1) { }
+
+ ScopeChainNode *next;
+ ObjectImp *object;
+ int refCount;
+ };
+
+/**
+* A scope chain object.
+*/
+ class KJS_EXPORT ScopeChain {
+ public:
+ 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; }
+ ObjectImp *top() const { return _node->object; }
+ ObjectImp *bottom() const { const ScopeChainNode *n = _node;
+ while (n->next) n = n->next;
+ return n->object; }
+
+ void clear() { deref(); _node = 0; }
+ void push(ObjectImp *);
+ void pop();
+
+ void mark();
+
+ private:
+ ScopeChainNode *_node;
+
+ void deref() { if (_node && --_node->refCount == 0) release(); }
+ void ref() const;
+
+ void release();
+ };
+
+} // namespace KJS
+
+#endif // KJS_SCOPE_CHAIN_H
diff --git a/kjs/simple_number.h b/kjs/simple_number.h
new file mode 100644
index 000000000..1f5830dff
--- /dev/null
+++ b/kjs/simple_number.h
@@ -0,0 +1,52 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_SIMPLE_NUMBER_H_
+#define _KJS_SIMPLE_NUMBER_H_
+
+#include <math.h>
+#include <string.h>
+
+#define IS_NEGATIVE_ZERO(num) (num == 0.0 && !memcmp(&num, &SimpleNumber::negZero, sizeof(double)))
+
+namespace KJS {
+ class ValueImp;
+
+ class SimpleNumber {
+ public:
+ enum { tag = 1, shift = 2, mask = (1 << shift) - 1, sign = 1L << (sizeof(long) * 8 - 1 ), max = (1L << ((sizeof(long) * 8 - 1) - shift)) - 1, min = -max - 1, imax = (1L << ((sizeof(int) * 8 - 1) - shift)) - 1, imin = -imax - 1 };
+
+ static inline bool is(const ValueImp *imp) { return ((long)imp & mask) == tag; }
+ static inline long value(const ValueImp *imp) { return ((long)imp >> shift) | (((long)imp & sign) ? ~max : 0); }
+
+ static inline bool fits(int i) { return i <= imax && i >= imin; }
+ static inline bool fits(unsigned i) { return i <= (unsigned)max; }
+ static inline bool fits(long i) { return i <= max && i >= min; }
+ static inline bool fits(unsigned long i) { return i <= (unsigned)max; }
+ static inline bool fits(double d) { return d >= min && d <= max && d == (double)(long)d && !IS_NEGATIVE_ZERO(d); }
+ static inline ValueImp *make(long i) { return (ValueImp *)((i << shift) | tag); }
+
+ static double negZero;
+ };
+}
+
+#endif
diff --git a/kjs/string_object.cpp b/kjs/string_object.cpp
new file mode 100644
index 000000000..b56620a8c
--- /dev/null
+++ b/kjs/string_object.cpp
@@ -0,0 +1,701 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
+#include "regexp.h"
+#include "regexp_object.h"
+#include "string_object.h"
+#include "error_object.h"
+#include <stdio.h>
+#include "string_object.lut.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_BITYPES_H
+#include <sys/bitypes.h> /* For uintXX_t on Tru64 */
+#endif
+
+using namespace KJS;
+
+// ------------------------------ StringInstanceImp ----------------------------
+
+const ClassInfo StringInstanceImp::info = {"String", 0, 0, 0};
+
+StringInstanceImp::StringInstanceImp(ObjectImp *proto)
+ : ObjectImp(proto)
+{
+ setInternalValue(String(""));
+}
+
+StringInstanceImp::StringInstanceImp(ObjectImp *proto, const UString &string)
+ : ObjectImp(proto)
+{
+ setInternalValue(String(string));
+}
+
+Value StringInstanceImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == lengthPropertyName)
+ return Number(internalValue().toString(exec).size());
+
+ bool ok;
+ const unsigned index = propertyName.toArrayIndex(&ok);
+ if (ok) {
+ const UString s = internalValue().toString(exec);
+ const unsigned length = s.size();
+ if (index < length) {
+ const UChar c = s[index];
+ return String(UString(&c, 1));
+ }
+ }
+
+ return ObjectImp::get(exec, propertyName);
+}
+
+void StringInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
+{
+ if (propertyName == lengthPropertyName)
+ return;
+ ObjectImp::put(exec, propertyName, value, attr);
+}
+
+bool StringInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
+{
+ if (propertyName == lengthPropertyName)
+ return true;
+
+ bool ok;
+ unsigned index = propertyName.toULong(&ok);
+ if (ok && index < (unsigned)internalValue().toString(exec).size())
+ return true;
+
+ return ObjectImp::hasProperty(exec, propertyName);
+}
+
+bool StringInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
+{
+ if (propertyName == lengthPropertyName)
+ return false;
+
+ bool ok;
+ unsigned index = propertyName.toULong(&ok);
+ if (ok && index < (unsigned)internalValue().toString(exec).size())
+ return false;
+
+ return ObjectImp::deleteProperty(exec, propertyName);
+}
+
+ReferenceList StringInstanceImp::propList(ExecState *exec, bool recursive)
+{
+ ReferenceList properties = ObjectImp::propList(exec,recursive);
+
+ UString str = internalValue().toString(exec);
+ for (int i = 0; i < str.size(); i++)
+ if (!ObjectImp::hasProperty(exec,Identifier::from(i)))
+ properties.append(Reference(this, i));
+
+ return properties;
+}
+
+// ------------------------------ StringPrototypeImp ---------------------------
+const ClassInfo StringPrototypeImp::info = {"String", &StringInstanceImp::info, &stringTable, 0};
+/* Source for string_object.lut.h
+@begin stringTable 28
+ toString StringProtoFuncImp::ToString DontEnum|Function 0
+ valueOf StringProtoFuncImp::ValueOf DontEnum|Function 0
+ charAt StringProtoFuncImp::CharAt DontEnum|Function 1
+ charCodeAt StringProtoFuncImp::CharCodeAt DontEnum|Function 1
+ concat StringProtoFuncImp::Concat DontEnum|Function 1
+ indexOf StringProtoFuncImp::IndexOf DontEnum|Function 1
+ lastIndexOf StringProtoFuncImp::LastIndexOf DontEnum|Function 1
+ match StringProtoFuncImp::Match DontEnum|Function 1
+ replace StringProtoFuncImp::Replace DontEnum|Function 2
+ search StringProtoFuncImp::Search DontEnum|Function 1
+ slice StringProtoFuncImp::Slice DontEnum|Function 2
+ split StringProtoFuncImp::Split DontEnum|Function 2
+ substr StringProtoFuncImp::Substr DontEnum|Function 2
+ substring StringProtoFuncImp::Substring DontEnum|Function 2
+ toLowerCase StringProtoFuncImp::ToLowerCase DontEnum|Function 0
+ toUpperCase StringProtoFuncImp::ToUpperCase DontEnum|Function 0
+ toLocaleLowerCase StringProtoFuncImp::ToLocaleLowerCase DontEnum|Function 0
+ toLocaleUpperCase StringProtoFuncImp::ToLocaleUpperCase DontEnum|Function 0
+ localeCompare StringProtoFuncImp::LocaleCompare DontEnum|Function 1
+#
+# Under here: html extension, should only exist if KJS_PURE_ECMA is not defined
+# I guess we need to generate two hashtables in the .lut.h file, and use #ifdef
+# to select the right one... TODO. #####
+ big StringProtoFuncImp::Big DontEnum|Function 0
+ small StringProtoFuncImp::Small DontEnum|Function 0
+ blink StringProtoFuncImp::Blink DontEnum|Function 0
+ bold StringProtoFuncImp::Bold DontEnum|Function 0
+ fixed StringProtoFuncImp::Fixed DontEnum|Function 0
+ italics StringProtoFuncImp::Italics DontEnum|Function 0
+ strike StringProtoFuncImp::Strike DontEnum|Function 0
+ sub StringProtoFuncImp::Sub DontEnum|Function 0
+ sup StringProtoFuncImp::Sup DontEnum|Function 0
+ fontcolor StringProtoFuncImp::Fontcolor DontEnum|Function 1
+ fontsize StringProtoFuncImp::Fontsize DontEnum|Function 1
+ anchor StringProtoFuncImp::Anchor DontEnum|Function 1
+ link StringProtoFuncImp::Link DontEnum|Function 1
+@end
+*/
+// ECMA 15.5.4
+StringPrototypeImp::StringPrototypeImp(ExecState * /*exec*/,
+ ObjectPrototypeImp *objProto)
+ : StringInstanceImp(objProto)
+{
+ Value protect(this);
+ // The constructor will be added later, after StringObjectImp has been built
+ putDirect(lengthPropertyName, NumberImp::zero(), DontDelete|ReadOnly|DontEnum);
+
+}
+
+Value StringPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
+{
+ return lookupGetFunction<StringProtoFuncImp, StringInstanceImp>( exec, propertyName, &stringTable, this );
+}
+
+// ------------------------------ StringProtoFuncImp ---------------------------
+
+StringProtoFuncImp::StringProtoFuncImp(ExecState *exec, int i, int len)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
+ ), id(i)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
+}
+
+bool StringProtoFuncImp::implementsCall() const
+{
+ return true;
+}
+
+// ### use as fallback only. implement locale aware version.
+static inline int localeCompare(const UString &a, const UString &b)
+{
+ // ### other browsers have more detailed return values than -1, 0 and 1
+ return compare(a, b);
+}
+
+// ECMA 15.5.4.2 - 15.5.4.20
+Value StringProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ Value result;
+
+ // toString and valueOf are no generic functions.
+ if (id == ToString || id == ValueOf) {
+ KJS_CHECK_THIS( StringInstanceImp, thisObj );
+
+ return String(thisObj.internalValue().toString(exec));
+ }
+
+ int n, m;
+ UString u2, u3;
+ double dpos;
+ int pos, p0, i;
+ double d = 0.0;
+
+ UString s = thisObj.toString(exec);
+
+ int len = s.size();
+ Value a0 = args[0];
+ Value a1 = args[1];
+
+ switch (id) {
+ case ToString:
+ case ValueOf:
+ // handled above
+ break;
+ case CharAt:
+ pos = a0.type() == UndefinedType ? 0 : a0.toInteger(exec);
+ if (pos < 0 || pos >= len)
+ s = "";
+ else
+ s = s.substr(pos, 1);
+ result = String(s);
+ break;
+ case CharCodeAt:
+ pos = a0.type() == UndefinedType ? 0 : a0.toInteger(exec);
+ if (pos < 0 || pos >= len)
+ d = NaN;
+ else {
+ UChar c = s[pos];
+ d = (c.high() << 8) + c.low();
+ }
+ result = Number(d);
+ break;
+ case Concat: {
+ ListIterator it = args.begin();
+ for ( ; it != args.end() ; ++it) {
+ s += it->dispatchToString(exec);
+ }
+ result = String(s);
+ break;
+ }
+ case IndexOf:
+ u2 = a0.toString(exec);
+ if (a1.type() == UndefinedType)
+ pos = 0;
+ else
+ pos = a1.toInteger(exec);
+ d = s.find(u2, pos);
+ result = Number(d);
+ break;
+ case LastIndexOf:
+ u2 = a0.toString(exec);
+ d = a1.toNumber(exec);
+ if (a1.type() == UndefinedType || KJS::isNaN(d))
+ dpos = len;
+ else {
+ dpos = d;
+ if (dpos < 0)
+ dpos = 0;
+ else if (dpos > len)
+ dpos = len;
+ }
+ result = Number(s.rfind(u2, int(dpos)));
+ break;
+ case Match:
+ case Search: {
+ RegExp *reg, *tmpReg = 0;
+ RegExpImp *imp = 0;
+ if (a0.isA(ObjectType) && a0.toObject(exec).inherits(&RegExpImp::info))
+ {
+ imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
+ reg = imp->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), RegExp::None);
+ }
+ if (!reg->isValid()) {
+ delete tmpReg;
+ Object err = Error::create(exec, SyntaxError,
+ "Invalid regular expression");
+ exec->setException(err);
+ return err;
+ }
+ RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->interpreter()->builtinRegExp().imp());
+ int **ovector = regExpObj->registerRegexp(reg, s);
+ reg->prepareMatch(s);
+ UString mstr = reg->match(s, -1, &pos, ovector);
+ if (id == Search) {
+ result = Number(pos);
+ } else { // Match
+ if (mstr.isNull()) {
+ result = Null(); // no match
+ } else if ((reg->flags() & RegExp::Global) == 0) {
+ // case without 'g' flag is handled like RegExp.prototype.exec
+ regExpObj->setSubPatterns(reg->subPatterns());
+ result = regExpObj->arrayOfMatches(exec,mstr);
+ } else {
+ // return array of matches
+ List list;
+ while (pos >= 0) {
+ list.append(String(mstr));
+ pos += mstr.isEmpty() ? 1 : mstr.size();
+ delete [] *ovector;
+ mstr = reg->match(s, pos, &pos, ovector);
+ }
+ result = exec->lexicalInterpreter()->builtinArray().construct(exec, list);
+ }
+ }
+ reg->doneMatch();
+ delete tmpReg;
+ break;
+ }
+ case Replace:
+ if (a0.type() == ObjectType && a0.toObject(exec).inherits(&RegExpImp::info)) {
+ RegExpImp* imp = static_cast<RegExpImp *>( a0.toObject(exec).imp() );
+ RegExp *reg = imp->regExp();
+ bool global = false;
+ Value tmp = imp->get(exec,"global");
+ if (tmp.type() != UndefinedType && tmp.toBoolean(exec) == true)
+ global = true;
+
+ RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp().imp());
+ int lastIndex = 0;
+ Object o1;
+ // Test if 2nd arg is a function (new in JS 1.3)
+ if ( a1.type() == ObjectType && a1.toObject(exec).implementsCall() )
+ o1 = a1.toObject(exec);
+ else
+ u3 = a1.toString(exec); // 2nd arg is the replacement string
+
+ UString out;
+
+ // This is either a loop (if global is set) or a one-way (if not).
+ reg->prepareMatch(s);
+ do {
+ int **ovector = regExpObj->registerRegexp( reg, s );
+ UString mstr = reg->match(s, lastIndex, &pos, ovector);
+ regExpObj->setSubPatterns(reg->subPatterns());
+ if (pos == -1)
+ break;
+
+ len = mstr.size();
+
+ UString rstr;
+ // Prepare replacement
+ if (!o1.isValid())
+ {
+ rstr = u3;
+ bool ok;
+ // check if u3 matches $1 or $2 etc
+ for (int i = 0; (i = rstr.find(UString("$"), i)) != -1; i++) {
+ if (i+1<rstr.size() && rstr[i+1] == '$') { // "$$" -> "$"
+ rstr = rstr.substr(0,i) + "$" + rstr.substr(i+2);
+ continue;
+ }
+ // Assume number part is one char exactly
+ unsigned long pos = rstr.substr(i+1,1).toULong(&ok, false /* tolerate empty string */);
+ if (ok && pos <= (unsigned)reg->subPatterns()) {
+ rstr = rstr.substr(0,i)
+ + s.substr((*ovector)[2*pos],
+ (*ovector)[2*pos+1]-(*ovector)[2*pos])
+ + rstr.substr(i+2);
+ i += (*ovector)[2*pos+1]-(*ovector)[2*pos] - 1; // -1 offsets i++
+ }
+ }
+ } else // 2nd arg is a function call. Spec from http://devedge.netscape.com/library/manuals/2000/javascript/1.5/reference/string.html#1194258
+ {
+ List l;
+ l.append(String(mstr)); // First arg: complete matched substring
+ // Then the submatch strings
+ for ( unsigned int sub = 1; sub <= reg->subPatterns() ; ++sub )
+ l.append( String( s.substr((*ovector)[2*sub],
+ (*ovector)[2*sub+1]-(*ovector)[2*sub]) ) );
+ l.append(Number(pos)); // The offset within the string where the match occurred
+ l.append(String(s)); // Last arg: the string itself. Can't see the difference with the 1st arg!
+ Object thisObj = exec->interpreter()->globalObject();
+ rstr = o1.call( exec, thisObj, l ).toString(exec);
+ }
+
+ // Append the stuff we skipped over to get to the match --
+ // that would be [lastIndex, pos) of the original..
+ if (pos != lastIndex)
+ out += s.substr(lastIndex, pos - lastIndex);
+
+ // Append the replacement..
+ out += rstr;
+
+ lastIndex = pos + len; // Skip over the matched stuff...
+ } while (global);
+
+ // Append the rest of the string to the output...
+ if (lastIndex == 0 && out.size() == 0) // Don't copy stuff if nothing changed
+ out = s;
+ else
+ out += s.substr(lastIndex, s.size() - lastIndex);
+
+ reg->doneMatch();
+
+ result = String(out);
+ } else { // First arg is a string
+ u2 = a0.toString(exec);
+ pos = s.find(u2);
+ len = u2.size();
+ // Do the replacement
+ if (pos == -1)
+ result = String(s);
+ else {
+ u3 = s.substr(0, pos) + a1.toString(exec) +
+ s.substr(pos + len);
+ result = String(u3);
+ }
+ }
+ break;
+ case Slice: // http://developer.netscape.com/docs/manuals/js/client/jsref/string.htm#1194366 or 15.5.4.13
+ {
+ // The arg processing is very much like ArrayProtoFunc::Slice
+ int begin = args[0].toUInt32(exec);
+ int end = len;
+ if (args[1].type() != UndefinedType) {
+ end = args[1].toInteger(exec);
+ }
+ int from = begin < 0 ? len + begin : begin;
+ int to = end < 0 ? len + end : end;
+ if (to > from && to > 0 && from < len) {
+ if (from < 0) {
+ from = 0;
+ }
+ if (to > len) {
+ to = len;
+ }
+ result = String(s.substr(from, to - from));
+ } else {
+ result = String("");
+ }
+ break;
+ }
+ case Split: {
+ Object constructor = exec->lexicalInterpreter()->builtinArray();
+ Object res = Object::dynamicCast(constructor.construct(exec,List::empty()));
+ result = res;
+ i = p0 = 0;
+ uint32_t limit = (a1.type() != UndefinedType) ? a1.toUInt32(exec) : 0xFFFFFFFFU;
+ if (a0.type() == ObjectType && Object::dynamicCast(a0).inherits(&RegExpImp::info)) {
+ Object obj0 = Object::dynamicCast(a0);
+ RegExp reg(obj0.get(exec,"source").toString(exec));
+ reg.prepareMatch(s);
+ if (s.isEmpty() && !reg.match(s, 0).isNull()) {
+ // empty string matched by regexp -> empty array
+ reg.doneMatch();
+ res.put(exec, lengthPropertyName, Number(0), DontDelete|ReadOnly|DontEnum);
+ break;
+ }
+ pos = 0;
+ while (static_cast<uint32_t>(i) != limit && pos < s.size()) {
+ // TODO: back references
+ int mpos;
+ int *ovector = 0L;
+ UString mstr = reg.match(s, pos, &mpos, &ovector);
+ delete [] ovector; ovector = 0L;
+ if (mpos < 0)
+ break;
+ pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
+ if (mpos != p0 || !mstr.isEmpty()) {
+ res.put(exec,i, String(s.substr(p0, mpos-p0)));
+ p0 = mpos + mstr.size();
+ i++;
+ }
+ }
+ reg.doneMatch();
+ } else {
+ u2 = a0.toString(exec);
+ if (u2.isEmpty()) {
+ if (s.isEmpty()) {
+ // empty separator matches empty string -> empty array
+ put(exec,lengthPropertyName, Number(0));
+ break;
+ } else {
+ while (static_cast<uint32_t>(i) != limit && i < s.size()-1)
+ res.put(exec,i++, String(s.substr(p0++, 1)));
+ }
+ } else {
+ while (static_cast<uint32_t>(i) != limit && (pos = s.find(u2, p0)) >= 0) {
+ res.put(exec,i, String(s.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++, String(s.substr(p0)));
+ res.put(exec,lengthPropertyName, Number(i));
+ }
+ break;
+ case Substr: {
+ n = a0.toInteger(exec);
+ m = a1.toInteger(exec);
+ int d, d2;
+ if (n >= 0)
+ d = n;
+ else
+ d = maxInt(len + n, 0);
+ if (a1.type() == UndefinedType)
+ d2 = len - d;
+ else
+ d2 = minInt(maxInt(m, 0), len - d);
+ result = String(s.substr(d, d2));
+ break;
+ }
+ case Substring: {
+ double start = a0.toNumber(exec);
+ double end = a1.toNumber(exec);
+ if (KJS::isNaN(start))
+ start = 0;
+ if (KJS::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.type() == UndefinedType)
+ end = len;
+ if (start > end) {
+ double temp = end;
+ end = start;
+ start = temp;
+ }
+ result = String(s.substr((int)start, (int)end-(int)start));
+ }
+ break;
+ case ToLowerCase:
+ case ToLocaleLowerCase: // FIXME: To get this 100% right we need to detect Turkish and change I to lowercase i without a dot.
+ for (i = 0; i < len; i++)
+ s[i] = s[i].toLower();
+ result = String(s);
+ break;
+ case ToUpperCase:
+ case ToLocaleUpperCase: // FIXME: To get this 100% right we need to detect Turkish and change i to uppercase I with a dot.
+ for (i = 0; i < len; i++)
+ s[i] = s[i].toUpper();
+ result = String(s);
+ break;
+ case LocaleCompare:
+ return Number(localeCompare(s, a0.toString(exec)));
+#ifndef KJS_PURE_ECMA
+ case Big:
+ result = String("<big>" + s + "</big>");
+ break;
+ case Small:
+ result = String("<small>" + s + "</small>");
+ break;
+ case Blink:
+ result = String("<blink>" + s + "</blink>");
+ break;
+ case Bold:
+ result = String("<b>" + s + "</b>");
+ break;
+ case Fixed:
+ result = String("<tt>" + s + "</tt>");
+ break;
+ case Italics:
+ result = String("<i>" + s + "</i>");
+ break;
+ case Strike:
+ result = String("<strike>" + s + "</strike>");
+ break;
+ case Sub:
+ result = String("<sub>" + s + "</sub>");
+ break;
+ case Sup:
+ result = String("<sup>" + s + "</sup>");
+ break;
+ case Fontcolor:
+ result = String("<font color=\"" + a0.toString(exec) + "\">" + s + "</font>");
+ break;
+ case Fontsize:
+ result = String("<font size=\"" + a0.toString(exec) + "\">" + s + "</font>");
+ break;
+ case Anchor:
+ result = String("<a name=\"" + a0.toString(exec) + "\">" + s + "</a>");
+ break;
+ case Link:
+ result = String("<a href=\"" + a0.toString(exec) + "\">" + s + "</a>");
+ break;
+#endif
+ }
+
+ return result;
+}
+
+// ------------------------------ StringObjectImp ------------------------------
+
+StringObjectImp::StringObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ StringPrototypeImp *stringProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ // ECMA 15.5.3.1 String.prototype
+ putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly);
+
+ putDirect("fromCharCode", new StringObjectFuncImp(exec,funcProto), DontEnum);
+
+ // no. of arguments for constructor
+ putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
+}
+
+
+bool StringObjectImp::implementsConstruct() const
+{
+ return true;
+}
+
+// ECMA 15.5.2
+Object StringObjectImp::construct(ExecState *exec, const List &args)
+{
+ ObjectImp *proto = exec->lexicalInterpreter()->builtinStringPrototype().imp();
+ if (args.size() == 0)
+ return Object(new StringInstanceImp(proto));
+ return Object(new StringInstanceImp(proto, args.begin()->dispatchToString(exec)));
+}
+
+bool StringObjectImp::implementsCall() const
+{
+ return true;
+}
+
+// ECMA 15.5.1
+Value StringObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ if (args.isEmpty())
+ return String("");
+ else {
+ Value v = args[0];
+ return String(v.toString(exec));
+ }
+}
+
+// ------------------------------ StringObjectFuncImp --------------------------
+
+// ECMA 15.5.3.2 fromCharCode()
+StringObjectFuncImp::StringObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ putDirect(lengthPropertyName, NumberImp::one(), DontDelete|ReadOnly|DontEnum);
+}
+
+bool StringObjectFuncImp::implementsCall() const
+{
+ return true;
+}
+
+Value StringObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ UString s;
+ if (args.size()) {
+ UChar *buf = new UChar[args.size()];
+ UChar *p = buf;
+ ListIterator it = args.begin();
+ while (it != args.end()) {
+ unsigned short u = it->toUInt16(exec);
+ *p++ = UChar(u);
+ it++;
+ }
+ s = UString(buf, args.size(), false);
+ } else
+ s = "";
+
+ return String(s);
+}
diff --git a/kjs/string_object.h b/kjs/string_object.h
new file mode 100644
index 000000000..d1c87ddf4
--- /dev/null
+++ b/kjs/string_object.h
@@ -0,0 +1,119 @@
+// -*- 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 "internal.h"
+#include "function_object.h"
+
+namespace KJS {
+
+ class StringInstanceImp : public ObjectImp {
+ public:
+ StringInstanceImp(ObjectImp *proto);
+ StringInstanceImp(ObjectImp *proto, const UString &string);
+
+ virtual Value get(ExecState *exec, const Identifier &propertyName) const;
+ virtual void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr = None);
+ virtual bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
+ virtual bool deleteProperty(ExecState *exec, const Identifier &propertyName);
+ virtual ReferenceList propList(ExecState *exec, bool recursive);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of String.prototype (and thus all objects created
+ * with the String constructor
+ */
+ class StringPrototypeImp : public StringInstanceImp {
+ public:
+ StringPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto);
+ Value get(ExecState *exec, const Identifier &p) const;
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * String.prototype object
+ */
+ class StringProtoFuncImp : public InternalFunctionImp {
+ public:
+ StringProtoFuncImp(ExecState *exec, int i, int len);
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { ToString, ValueOf, CharAt, CharCodeAt, Concat, IndexOf, LastIndexOf,
+ Match, Replace, Search, Slice, Split,
+ Substr, Substring, FromCharCode, ToLowerCase, ToUpperCase,
+ ToLocaleLowerCase, ToLocaleUpperCase, LocaleCompare
+#ifndef KJS_PURE_ECMA
+ , Big, Small, Blink, Bold, Fixed, Italics, Strike, Sub, Sup,
+ Fontcolor, Fontsize, Anchor, Link
+#endif
+ };
+ private:
+ int id;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "String" property
+ */
+ class StringObjectImp : public InternalFunctionImp {
+ public:
+ StringObjectImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto,
+ StringPrototypeImp *stringProto);
+
+ virtual bool implementsConstruct() const;
+ virtual Object construct(ExecState *exec, const List &args);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * String object
+ */
+ class StringObjectFuncImp : public InternalFunctionImp {
+ public:
+ StringObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+ };
+
+} // namespace
+
+#endif
+
diff --git a/kjs/test.js b/kjs/test.js
new file mode 100644
index 000000000..f5bbf5ba3
--- /dev/null
+++ b/kjs/test.js
@@ -0,0 +1,29 @@
+var i = 0;
+
+function sum(a, b)
+{
+ debug("inside test()");
+ i = i + 1;
+ debug(a);
+ debug(b);
+ return a + b;
+}
+
+s = sum(10, sum(20, 30));
+debug("s = " + s);
+debug("i = " + i);
+
+var a = new Array(11, 22, 33, 44);
+a.length = 2;
+a[4] = 'apple';
+
+for(i = 0; i != a.length; i++)
+ debug("a[" + i + "] = " + a[i]);
+
+var b = new Boolean(1==1);
+b.toString=Object.prototype.toString;
+debug("b = " + b.toString());
+
+// regular expression
+rx = /b*c/;
+debug(rx.exec("abbbcd"));
diff --git a/kjs/testkjs.cpp b/kjs/testkjs.cpp
new file mode 100644
index 000000000..fcea6687f
--- /dev/null
+++ b/kjs/testkjs.cpp
@@ -0,0 +1,173 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+
+using namespace KJS;
+
+class TestFunctionImp : public ObjectImp {
+public:
+ TestFunctionImp(int i, int length);
+ virtual bool implementsCall() const { return true; }
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum { Print, Debug, Quit };
+
+private:
+ int id;
+};
+
+TestFunctionImp::TestFunctionImp(int i, int length) : ObjectImp(), id(i)
+{
+ putDirect(lengthPropertyName,length,DontDelete|ReadOnly|DontEnum);
+}
+
+Value TestFunctionImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ switch (id) {
+ case Print:
+ case Debug:
+ fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii());
+ return Undefined();
+ case Quit:
+ exit(0);
+ return Undefined();
+ default:
+ break;
+ }
+
+ return Undefined();
+}
+
+class VersionFunctionImp : public ObjectImp {
+public:
+ VersionFunctionImp() : ObjectImp() {}
+ virtual bool implementsCall() const { return true; }
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+};
+
+Value VersionFunctionImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
+{
+ // We need this function for compatibility with the Mozilla JS tests but for now
+ // we don't actually do any version-specific handling
+ return Undefined();
+}
+
+class GlobalImp : public ObjectImp {
+public:
+ virtual UString className() const { return "global"; }
+};
+
+int main(int argc, char **argv)
+{
+ // expecting a filename
+ if (argc < 2) {
+ fprintf(stderr, "You have to specify at least one filename\n");
+ return -1;
+ }
+
+ bool ret = true;
+ {
+ Object global(new GlobalImp());
+
+ // create interpreter
+ Interpreter interp(global);
+ // add debug() function
+ global.put(interp.globalExec(), "debug", Object(new TestFunctionImp(TestFunctionImp::Debug,1)));
+ // add "print" for compatibility with the mozilla js shell
+ global.put(interp.globalExec(), "print", Object(new TestFunctionImp(TestFunctionImp::Print,1)));
+ // add "quit" for compatibility with the mozilla js shell
+ global.put(interp.globalExec(), "quit", Object(new TestFunctionImp(TestFunctionImp::Quit,0)));
+ // add "version" for compatibility with the mozilla js shell
+ global.put(interp.globalExec(), "version", Object(new VersionFunctionImp()));
+
+ for (int i = 1; i < argc; i++) {
+ int code_len = 0;
+ int code_alloc = 1024;
+ char *code = (char*)malloc(code_alloc);
+
+ const char *file = argv[i];
+ if (strcmp(file, "-f") == 0)
+ continue;
+ FILE *f = fopen(file, "r");
+ if (!f) {
+ fprintf(stderr, "Error opening %s.\n", file);
+ return 2;
+ }
+
+ while (!feof(f) && !ferror(f)) {
+ size_t len = fread(code+code_len,1,code_alloc-code_len,f);
+ code_len += len;
+ if (code_len >= code_alloc) {
+ code_alloc *= 2;
+ code = (char*)realloc(code,code_alloc);
+ }
+ }
+ code = (char*)realloc(code,code_len+1);
+ code[code_len] = '\0';
+
+ // run
+ Completion comp(interp.evaluate(code));
+
+ fclose(f);
+
+ if (comp.complType() == Throw) {
+ ExecState *exec = interp.globalExec();
+ Value exVal = comp.value();
+ char *msg = exVal.toString(exec).ascii();
+ int lineno = -1;
+ if (exVal.type() == ObjectType) {
+ Value lineVal = Object::dynamicCast(exVal).get(exec,"line");
+ if (lineVal.type() == NumberType)
+ lineno = int(lineVal.toNumber(exec));
+ }
+ if (lineno != -1)
+ fprintf(stderr,"Exception, line %d: %s\n",lineno,msg);
+ else
+ fprintf(stderr,"Exception: %s\n",msg);
+ ret = false;
+ }
+ else if (comp.complType() == ReturnValue) {
+ char *msg = comp.value().toString(interp.globalExec()).ascii();
+ fprintf(stderr,"Return value: %s\n",msg);
+ }
+
+ free(code);
+ }
+
+ } // end block, so that Interpreter and global get deleted
+
+ if (ret)
+ fprintf(stderr, "OK.\n");
+
+#ifdef KJS_DEBUG_MEM
+ Interpreter::finalCheck();
+#endif
+ return ret ? 0 : 3;
+}
diff --git a/kjs/types.h b/kjs/types.h
new file mode 100644
index 000000000..603b2a26e
--- /dev/null
+++ b/kjs/types.h
@@ -0,0 +1,25 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "completion.h"
+#include "list.h"
diff --git a/kjs/ustring.cpp b/kjs/ustring.cpp
new file mode 100644
index 000000000..36f201863
--- /dev/null
+++ b/kjs/ustring.cpp
@@ -0,0 +1,983 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include "ustring.h"
+#include "operations.h"
+#include "identifier.h"
+#include <math.h>
+#include "dtoa.h"
+
+namespace KJS {
+ extern const double NaN;
+ extern const double Inf;
+}
+
+using namespace KJS;
+
+CString::CString(const char *c)
+{
+ length = strlen(c);
+ data = new char[length+1];
+ memcpy(data, c, length + 1);
+}
+
+CString::CString(const char *c, int len)
+{
+ length = len;
+ data = new char[len+1];
+ memcpy(data, c, len);
+ data[len] = 0;
+}
+
+CString::CString(const CString &b)
+{
+ length = b.length;
+ data = new char[length+1];
+ memcpy(data, b.data, length + 1);
+}
+
+CString::~CString()
+{
+ delete [] data;
+}
+
+CString &CString::append(const CString &t)
+{
+ char *n = new char[length + t.length + 1];
+ if (length)
+ memcpy(n, data, length);
+ if (t.length)
+ memcpy(n+length, t.data, t.length);
+ length += t.length;
+ n[length] = 0;
+
+ delete [] data;
+ data = n;
+
+ return *this;
+}
+
+CString &CString::operator=(const char *c)
+{
+ delete [] data;
+ length = strlen(c);
+ data = new char[length+1];
+ memcpy(data, c, length + 1);
+
+ return *this;
+}
+
+CString &CString::operator=(const CString &str)
+{
+ if (this == &str)
+ return *this;
+
+ delete [] data;
+ length = str.length;
+ if (str.data) {
+ data = new char[length + 1];
+ memcpy(data, str.data, length + 1);
+ }
+ else
+ data = 0;
+
+ return *this;
+}
+
+bool KJS::operator==(const KJS::CString& c1, const KJS::CString& c2)
+{
+ int len = c1.size();
+ return len == c2.size() && (len == 0 || memcmp(c1.c_str(), c2.c_str(), len) == 0);
+}
+
+UChar UChar::null((char)0);
+UString::Rep UString::Rep::null = { 0, 0, 0, 1, 1 };
+UString::Rep UString::Rep::empty = { 0, 0, 0, 1, 1 };
+UString UString::null;
+static const int normalStatBufferSize = 4096;
+static char *statBuffer = 0;
+static int statBufferSize = 0;
+
+UChar UChar::toLower() const
+{
+ // ### properly support unicode tolower
+ if (uc >= 256)
+ return *this;
+
+ // tolower is locale-dependent, don't use it.
+ return static_cast<unsigned char>( ( ( uc >= 'A' ) && ( uc <= 'Z' ) ) ? ( (int)uc + 'a' - 'A' ) : uc );
+}
+
+UChar UChar::toUpper() const
+{
+ if (uc >= 256)
+ return *this;
+
+ // toupper is locale-dependent, don't use it.
+ return static_cast<unsigned char>( ( ( uc >= 'a' ) && ( uc <= 'z' ) ) ? ( (int)uc + 'A' - 'a' ) : uc );
+}
+
+UCharReference& UCharReference::operator=(UChar c)
+{
+ str->detach();
+ if (offset < str->rep->len)
+ *(str->rep->dat + offset) = c;
+ /* TODO: lengthen string ? */
+ return *this;
+}
+
+UChar& UCharReference::ref() const
+{
+ if (offset < str->rep->len)
+ return *(str->rep->dat + offset);
+ else
+ return UChar::null;
+}
+
+// return an uninitialized UChar array of size s
+static inline UChar* allocateChars(int s)
+{
+ // work around default UChar constructor code
+ return reinterpret_cast<UChar*>(new short[s]);
+}
+
+UString::Rep *UString::Rep::create(UChar *d, int l)
+{
+ Rep *r = new Rep;
+ r->dat = d;
+ r->len = l;
+ r->capacity = l;
+ r->rc = 1;
+ r->_hash = 0;
+ return r;
+}
+
+void UString::Rep::destroy()
+{
+ if (capacity == capacityForIdentifier)
+ Identifier::remove(this);
+ delete [] dat;
+ delete this;
+}
+
+// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
+// or anything like that.
+const unsigned PHI = 0x9e3779b9U;
+
+// This hash algorithm comes from:
+// http://burtleburtle.net/bob/hash/hashfaq.html
+// http://burtleburtle.net/bob/hash/doobs.html
+unsigned UString::Rep::computeHash(const UChar *s, int length)
+{
+ int prefixLength = length < 8 ? length : 8;
+ int suffixPosition = length < 16 ? 8 : length - 8;
+
+ unsigned h = PHI;
+ h += length;
+ h += (h << 10);
+ h ^= (h << 6);
+
+ for (int i = 0; i < prefixLength; i++) {
+ h += s[i].uc;
+ h += (h << 10);
+ h ^= (h << 6);
+ }
+ for (int i = suffixPosition; i < length; i++){
+ h += s[i].uc;
+ h += (h << 10);
+ h ^= (h << 6);
+ }
+
+ h += (h << 3);
+ h ^= (h >> 11);
+ h += (h << 15);
+
+ if (h == 0)
+ h = 0x80000000;
+
+ return h;
+}
+
+// This hash algorithm comes from:
+// http://burtleburtle.net/bob/hash/hashfaq.html
+// http://burtleburtle.net/bob/hash/doobs.html
+unsigned UString::Rep::computeHash(const char *s)
+{
+ int length = strlen(s);
+ int prefixLength = length < 8 ? length : 8;
+ int suffixPosition = length < 16 ? 8 : length - 8;
+
+ unsigned h = PHI;
+ h += length;
+ h += (h << 10);
+ h ^= (h << 6);
+
+ for (int i = 0; i < prefixLength; i++) {
+ h += (unsigned char)s[i];
+ h += (h << 10);
+ h ^= (h << 6);
+ }
+ for (int i = suffixPosition; i < length; i++) {
+ h += (unsigned char)s[i];
+ h += (h << 10);
+ h ^= (h << 6);
+ }
+
+ h += (h << 3);
+ h ^= (h >> 11);
+ h += (h << 15);
+
+ if (h == 0)
+ h = 0x80000000;
+
+ return h;
+}
+
+UString::UString()
+{
+ null.rep = &Rep::null;
+ attach(&Rep::null);
+}
+
+UString::UString(char c)
+{
+ UChar *d = allocateChars(1);
+ d[0] = c;
+ rep = Rep::create(d, 1);
+}
+
+UString::UString(const char *c)
+{
+ if (!c) {
+ attach(&Rep::null);
+ return;
+ }
+ int length = strlen(c);
+ if (length == 0) {
+ attach(&Rep::empty);
+ return;
+ }
+ UChar *d = new UChar[length];
+ for (int i = 0; i < length; i++)
+ d[i].uc = (unsigned char)c[i];
+ rep = Rep::create(d, length);
+}
+
+UString::UString(const UChar *c, int length)
+{
+ if (length == 0) {
+ attach(&Rep::empty);
+ return;
+ }
+ UChar *d = allocateChars(length);
+ memcpy(d, c, length * sizeof(UChar));
+ rep = Rep::create(d, length);
+}
+
+UString::UString(UChar *c, int length, bool copy)
+{
+ if (length == 0) {
+ attach(&Rep::empty);
+ return;
+ }
+ UChar *d;
+ if (copy) {
+ d = allocateChars(length);
+ memcpy(d, c, length * sizeof(UChar));
+ } else
+ d = c;
+ rep = Rep::create(d, length);
+}
+
+UString::UString(const UString &a, const UString &b)
+{
+ int aSize = a.size();
+ int bSize = b.size();
+ int length = aSize + bSize;
+ if (length == 0) {
+ attach(&Rep::empty);
+ return;
+ }
+ UChar *d = allocateChars(length);
+ memcpy(d, a.data(), aSize * sizeof(UChar));
+ memcpy(d + aSize, b.data(), bSize * sizeof(UChar));
+ rep = Rep::create(d, length);
+}
+
+UString UString::from(int i)
+{
+ return from((long)i);
+}
+
+UString UString::from(unsigned int u)
+{
+ UChar buf[20];
+ UChar *end = buf + 20;
+ UChar *p = end;
+
+ if (u == 0) {
+ *--p = '0';
+ } else {
+ while (u) {
+ *--p = (unsigned short)((u % 10) + '0');
+ u /= 10;
+ }
+ }
+
+ return UString(p, end - p);
+}
+
+UString UString::from(long l)
+{
+ UChar buf[20];
+ UChar *end = buf + 20;
+ UChar *p = end;
+
+ if (l == 0) {
+ *--p = '0';
+ } else {
+ bool negative = false;
+ if (l < 0) {
+ negative = true;
+ l = -l;
+ }
+ while (l) {
+ *--p = (unsigned short)((l % 10) + '0');
+ l /= 10;
+ }
+ if (negative) {
+ *--p = '-';
+ }
+ }
+
+ return UString(p, end - p);
+}
+
+UString UString::from(double d)
+{
+ char buf[80];
+ int decimalPoint;
+ int sign;
+
+ char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL);
+ int length = strlen(result);
+
+ int i = 0;
+ if (sign) {
+ buf[i++] = '-';
+ }
+
+ if (decimalPoint <= 0 && decimalPoint > -6) {
+ buf[i++] = '0';
+ buf[i++] = '.';
+ for (int j = decimalPoint; j < 0; j++) {
+ buf[i++] = '0';
+ }
+ strcpy(buf + i, result);
+ } else if (decimalPoint <= 21 && decimalPoint > 0) {
+ if (length <= decimalPoint) {
+ strcpy(buf + i, result);
+ i += length;
+ for (int j = 0; j < decimalPoint - length; j++) {
+ buf[i++] = '0';
+ }
+ buf[i] = '\0';
+ } else {
+ strncpy(buf + i, result, decimalPoint);
+ i += decimalPoint;
+ buf[i++] = '.';
+ strcpy(buf + i, result + decimalPoint);
+ }
+ } else if (result[0] < '0' || result[0] > '9') {
+ strcpy(buf + i, result);
+ } else {
+ buf[i++] = result[0];
+ if (length > 1) {
+ buf[i++] = '.';
+ strcpy(buf + i, result + 1);
+ i += length - 1;
+ }
+
+ buf[i++] = 'e';
+ buf[i++] = (decimalPoint >= 0) ? '+' : '-';
+ // decimalPoint can't be more than 3 digits decimal given the
+ // nature of float representation
+ int exponential = decimalPoint - 1;
+ if (exponential < 0) {
+ exponential = exponential * -1;
+ }
+ if (exponential >= 100) {
+ buf[i++] = '0' + exponential / 100;
+ }
+ if (exponential >= 10) {
+ buf[i++] = '0' + (exponential % 100) / 10;
+ }
+ buf[i++] = '0' + exponential % 10;
+ buf[i++] = '\0';
+ }
+
+ kjs_freedtoa(result);
+
+ return UString(buf);
+}
+
+UString &UString::append(const UString &t)
+{
+ int l = size();
+ int tLen = t.size();
+ int newLen = l + tLen;
+ if (rep->rc == 1 && newLen <= rep->capacity) {
+ memcpy(rep->dat+l, t.data(), tLen * sizeof(UChar));
+ rep->len = newLen;
+ rep->_hash = 0;
+ return *this;
+ }
+
+ int newCapacity = (newLen * 3 + 1) / 2;
+ UChar *n = allocateChars(newCapacity);
+ memcpy(n, data(), l * sizeof(UChar));
+ memcpy(n+l, t.data(), tLen * sizeof(UChar));
+ release();
+ rep = Rep::create(n, newLen);
+ rep->capacity = newCapacity;
+
+ return *this;
+}
+
+CString UString::cstring() const
+{
+ return ascii();
+}
+
+char *UString::ascii() const
+{
+ // Never make the buffer smaller than normalStatBufferSize.
+ // Thus we almost never need to reallocate.
+ int length = size();
+ int neededSize = length + 1;
+ if (neededSize < normalStatBufferSize) {
+ neededSize = normalStatBufferSize;
+ }
+ if (neededSize != statBufferSize) {
+ delete [] statBuffer;
+ statBuffer = new char [neededSize];
+ statBufferSize = neededSize;
+ }
+
+ const UChar *p = data();
+ char *q = statBuffer;
+ const UChar *limit = p + length;
+ while (p != limit) {
+ *q = p->uc;
+ ++p;
+ ++q;
+ }
+ *q = '\0';
+
+ return statBuffer;
+}
+
+#ifdef KJS_DEBUG_MEM
+void UString::globalClear()
+{
+ delete [] statBuffer;
+ statBuffer = 0;
+ statBufferSize = 0;
+}
+#endif
+
+UString &UString::operator=(const char *c)
+{
+ int l = c ? strlen(c) : 0;
+ UChar *d;
+ if (rep->rc == 1 && l <= rep->capacity) {
+ d = rep->dat;
+ rep->_hash = 0;
+ } else {
+ release();
+ d = allocateChars(l);
+ rep = Rep::create(d, l);
+ }
+ for (int i = 0; i < l; i++)
+ d[i].uc = (unsigned char)c[i];
+
+ return *this;
+}
+
+UString &UString::operator=(const UString &str)
+{
+ str.rep->ref();
+ release();
+ rep = str.rep;
+
+ return *this;
+}
+
+bool UString::is8Bit() const
+{
+ const UChar *u = data();
+ const UChar *limit = u + size();
+ while (u < limit) {
+ if (u->uc > 0xFF)
+ return false;
+ ++u;
+ }
+
+ return true;
+}
+
+UChar UString::operator[](int pos) const
+{
+ if (pos >= size())
+ return UChar::null;
+
+ return ((UChar *)data())[pos];
+}
+
+UCharReference UString::operator[](int pos)
+{
+ /* TODO: boundary check */
+ return UCharReference(this, pos);
+}
+
+static int skipInfString(const char *start)
+{
+ const char *c = start;
+ if (*c == '+' || *c == '-')
+ c++;
+ if (!strncmp(c,"Infinity",8))
+ return c+8-start;
+
+ while (*c >= '0' && *c <= '9')
+ c++;
+ const char * const at_dot = c;
+ if (*c == '.')
+ c++;
+ while (*c >= '0' && *c <= '9')
+ c++;
+
+ // don't accept a single dot as a number
+ if (c - at_dot == 1 && *at_dot == '.')
+ return at_dot-start;
+
+ if (*c != 'e')
+ return c-start;
+
+ c++;
+ if (*c == '+' || *c == '-')
+ c++;
+ while (*c >= '0' && *c <= '9')
+ c++;
+ return c-start;
+}
+
+double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
+{
+ double d;
+ double sign = 1;
+
+ // 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 (isspace(*c))
+ c++;
+
+ // empty string ?
+ if (*c == '\0')
+ return tolerateEmptyString ? 0.0 : NaN;
+
+ if (*c == '-') {
+ sign = -1;
+ c++;
+ }
+ else if (*c == '+') {
+ sign = 1;
+ c++;
+ }
+
+ // hex number ?
+ if (*c == '0' && (*(c+1) == 'x' || *(c+1) == 'X')) {
+ 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;
+ }
+ } else {
+ // regular number ?
+ char *end;
+ d = kjs_strtod(c, &end);
+ if ((d != 0.0 || end != c) && d != HUGE_VAL && d != -HUGE_VAL) {
+ c = end;
+ } else {
+ // infinity ?
+
+ int count = skipInfString(c);
+ if (count == 0)
+ return NaN;
+ d = Inf;
+ c += count;
+ }
+ }
+
+ // allow trailing white space
+ while (isspace(*c))
+ c++;
+ // don't allow anything after - unless tolerant=true
+ if (!tolerateTrailingJunk && *c != '\0')
+ return NaN;
+
+ return d*sign;
+}
+
+double UString::toDouble(bool tolerateTrailingJunk) const
+{
+ return toDouble(tolerateTrailingJunk, true);
+}
+
+double UString::toDouble() const
+{
+ return toDouble(false, true);
+}
+
+unsigned long UString::toULong(bool *ok, bool tolerateEmptyString) const
+{
+ double d = toDouble(false, tolerateEmptyString);
+ bool b = true;
+
+ if (isNaN(d) || d != static_cast<unsigned long>(d)) {
+ b = false;
+ d = 0;
+ }
+
+ if (ok)
+ *ok = b;
+
+ return static_cast<unsigned long>(d);
+}
+
+unsigned long UString::toULong(bool *ok) const
+{
+ return toULong(ok, true);
+}
+
+UString UString::toLower() const
+{
+ UString u = *this;
+ for (int i = 0; i < size(); i++)
+ u[i] = u[i].toLower();
+ return u;
+}
+
+UString UString::toUpper() const
+{
+ UString u = *this;
+ for (int i = 0; i < size(); i++)
+ u[i] = u[i].toUpper();
+ return u;
+}
+
+unsigned int UString::toUInt32(bool *ok) const
+{
+ double d = toDouble();
+ bool b = true;
+
+ if (isNaN(d) || d != static_cast<unsigned>(d)) {
+ b = false;
+ d = 0;
+ }
+
+ if (ok)
+ *ok = b;
+
+ return static_cast<unsigned>(d);
+}
+
+unsigned int UString::toStrictUInt32(bool *ok) const
+{
+ if (ok)
+ *ok = false;
+
+ // Empty string is not OK.
+ int len = rep->len;
+ if (len == 0)
+ return 0;
+ const UChar *p = rep->dat;
+ unsigned short c = p->unicode();
+
+ // If the first digit is 0, only 0 itself is OK.
+ if (c == '0') {
+ if (len == 1 && ok)
+ *ok = true;
+ return 0;
+ }
+
+ // Convert to UInt32, checking for overflow.
+ unsigned int i = 0;
+ while (1) {
+ // Process character, turning it into a digit.
+ if (c < '0' || c > '9')
+ return 0;
+ const unsigned d = c - '0';
+
+ // Multiply by 10, checking for overflow out of 32 bits.
+ if (i > 0xFFFFFFFFU / 10)
+ return 0;
+ i *= 10;
+
+ // Add in the digit, checking for overflow out of 32 bits.
+ const unsigned max = 0xFFFFFFFFU - d;
+ if (i > max)
+ return 0;
+ i += d;
+
+ // Handle end of string.
+ if (--len == 0) {
+ if (ok)
+ *ok = true;
+ return i;
+ }
+
+ // Get next character.
+ c = (++p)->unicode();
+ }
+}
+
+// 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.
+unsigned UString::toArrayIndex(bool *ok) const
+{
+ unsigned i = toStrictUInt32(ok);
+ if (i >= 0xFFFFFFFFU && ok)
+ *ok = false;
+ return i;
+}
+
+int UString::find(const UString &f, int pos) const
+{
+ int sz = size();
+ int fsz = f.size();
+ if (sz < fsz)
+ return -1;
+ if (pos < 0)
+ pos = 0;
+ if (fsz == 0)
+ return pos;
+ const UChar *end = data() + sz - fsz;
+ long 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 (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 (c-data());
+
+ return -1;
+}
+
+int UString::rfind(const UString &f, int pos) const
+{
+ int sz = size();
+ int fsz = f.size();
+ if (sz < fsz)
+ return -1;
+ if (pos < 0)
+ pos = 0;
+ if (pos > sz - fsz)
+ pos = sz - fsz;
+ if (fsz == 0)
+ return pos;
+ long 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 (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 (c-data());
+ }
+
+ return -1;
+}
+
+UString UString::substr(int pos, int len) const
+{
+ if (pos < 0)
+ pos = 0;
+ else if (pos >= (int) size())
+ pos = size();
+ if (len < 0)
+ len = size();
+ if (pos + len >= (int) size())
+ len = size() - pos;
+
+ UChar *tmp = allocateChars(len);
+ memcpy(tmp, data()+pos, len * sizeof(UChar));
+ UString result(tmp, len);
+ delete [] tmp;
+
+ return result;
+}
+
+void UString::attach(Rep *r)
+{
+ rep = r;
+ rep->ref();
+}
+
+void UString::detach()
+{
+ if (rep->rc > 1) {
+ int l = size();
+ UChar *n = allocateChars(l);
+ memcpy(n, data(), l * sizeof(UChar));
+ release();
+ rep = Rep::create(n, l);
+ }
+}
+
+void UString::release()
+{
+ rep->deref();
+}
+
+bool KJS::operator==(const UString& s1, const UString& s2)
+{
+ if (s1.rep->len != s2.rep->len)
+ return false;
+
+#ifndef NDEBUG
+ if ((s1.isNull() && s2.isEmpty() && !s2.isNull()) ||
+ (s2.isNull() && s1.isEmpty() && !s1.isNull()))
+ fprintf(stderr,
+ "KJS warning: comparison between empty and null string\n");
+#endif
+
+ return (memcmp(s1.rep->dat, s2.rep->dat,
+ s1.rep->len * sizeof(UChar)) == 0);
+}
+
+bool KJS::operator==(const UString& s1, const char *s2)
+{
+ if (s2 == 0) {
+ return s1.isEmpty();
+ }
+
+ const UChar *u = s1.data();
+ const UChar *uend = u + s1.size();
+ while (u != uend && *s2) {
+ if (u->uc != (unsigned char)*s2)
+ return false;
+ s2++;
+ u++;
+ }
+
+ return u == uend && *s2 == 0;
+}
+
+bool KJS::operator<(const UString& s1, const UString& s2)
+{
+ const int l1 = s1.size();
+ const int l2 = s2.size();
+ const int lmin = l1 < l2 ? l1 : l2;
+ const UChar *c1 = s1.data();
+ const UChar *c2 = s2.data();
+ int l = 0;
+ while (l < lmin && *c1 == *c2) {
+ c1++;
+ c2++;
+ l++;
+ }
+ if (l < lmin)
+ return (c1->uc < c2->uc);
+
+ return (l1 < l2);
+}
+
+int KJS::compare(const UString& s1, const UString& s2)
+{
+ const int l1 = s1.size();
+ const int l2 = s2.size();
+ const int lmin = l1 < l2 ? l1 : l2;
+ const UChar *c1 = s1.data();
+ const UChar *c2 = s2.data();
+ int l = 0;
+ while (l < lmin && *c1 == *c2) {
+ c1++;
+ c2++;
+ l++;
+ }
+ if (l < lmin)
+ return (c1->uc > c2->uc) ? 1 : -1;
+
+ if (l1 == l2) {
+ return 0;
+ }
+ return (l1 < l2) ? 1 : -1;
+}
diff --git a/kjs/ustring.h b/kjs/ustring.h
new file mode 100644
index 000000000..2cdd2db78
--- /dev/null
+++ b/kjs/ustring.h
@@ -0,0 +1,476 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * 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_USTRING_H_
+#define _KJS_USTRING_H_
+
+#include "global.h"
+
+/**
+ * @internal
+ */
+namespace DOM {
+ class DOMString;
+}
+class KJScript;
+class QString;
+class QConstString;
+
+namespace KJS {
+
+ class UCharReference;
+ 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 KJS_EXPORT UChar {
+ /**
+ * Construct a character with uninitialized value.
+ */
+ UChar();
+ UChar(char u);
+ UChar(unsigned char u);
+ /**
+ * 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(unsigned short u);
+ UChar(const UCharReference &c);
+ /**
+ * @return The higher byte of the character.
+ */
+ unsigned char high() const { return uc >> 8; }
+ /**
+ * @return The lower byte of the character.
+ */
+ unsigned char low() const { return uc; }
+ /**
+ * @return the 16 bit Unicode value of the character
+ */
+ unsigned short unicode() const { return uc; }
+ public:
+ /**
+ * @return The character converted to lower case.
+ */
+ UChar toLower() const;
+ /**
+ * @return The character converted to upper case.
+ */
+ UChar toUpper() const;
+ /**
+ * A static instance of UChar(0).
+ */
+ static UChar null;
+
+ unsigned short uc;
+ } KJS_PACKED;
+
+ 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 Dynamic reference to a string character.
+ *
+ * UCharReference is the dynamic counterpart of UChar. It's used when
+ * characters retrieved via index from a UString are used in an
+ * assignment expression (and therefore can't be treated as being const):
+ * \code
+ * UString s("hello world");
+ * s[0] = 'H';
+ * \endcode
+ *
+ * If that sounds confusing your best bet is to simply forget about the
+ * existence of this class and treat is as being identical to UChar.
+ */
+ class KJS_EXPORT UCharReference {
+ friend class UString;
+ UCharReference(UString *s, unsigned int off) : str(s), offset(off) { }
+ public:
+ /**
+ * Set the referenced character to c.
+ */
+ UCharReference& operator=(UChar c);
+ /**
+ * Same operator as above except the argument that it takes.
+ */
+ UCharReference& operator=(char c) { return operator=(UChar(c)); }
+ /**
+ * @return Unicode value.
+ */
+ unsigned short unicode() const { return ref().uc; }
+ /**
+ * @return Lower byte.
+ */
+ unsigned char low() const { return ref().uc; }
+ /**
+ * @return Higher byte.
+ */
+ unsigned char high() const { return ref().uc >> 8; }
+ /**
+ * @return Character converted to lower case.
+ */
+ UChar toLower() const { return ref().toLower(); }
+ /**
+ * @return Character converted to upper case.
+ */
+ UChar toUpper() const { return ref().toUpper(); }
+ private:
+ // not implemented, can only be constructed from UString
+ UCharReference();
+
+ UChar& ref() const;
+ UString *str;
+ int offset;
+ };
+
+ inline UChar::UChar(const UCharReference &c) : uc(c.unicode()) { }
+
+ /**
+ * @short 8 bit char based string class
+ */
+ class KJS_EXPORT CString {
+ public:
+ CString() : data(0L), length(0) { }
+ CString(const char *c);
+ CString(const char *c, int 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); }
+
+ int size() const { return length; }
+ const char *c_str() const { return data; }
+ private:
+ char *data;
+ int length;
+ };
+
+ /**
+ * @short Unicode string class
+ */
+ class KJS_EXPORT UString {
+ friend bool operator==(const UString&, const UString&);
+ friend class UCharReference;
+ friend class Identifier;
+ friend class PropertyMap;
+ friend class PropertyMapHashTableEntry;
+ /**
+ * @internal
+ */
+ struct KJS_EXPORT Rep {
+ friend class UString;
+ friend bool operator==(const UString&, const UString&);
+
+ static Rep *create(UChar *d, int l);
+ void destroy();
+
+ UChar *data() const { return dat; }
+ int size() const { return len; }
+
+ unsigned hash() const { if (_hash == 0) _hash = computeHash(dat, len); return _hash; }
+
+ static unsigned computeHash(const UChar *, int length);
+ static unsigned computeHash(const char *);
+
+ void ref() { ++rc; }
+ void deref() { if (--rc == 0) destroy(); }
+
+ UChar *dat;
+ int len;
+ int capacity;
+ int rc;
+ mutable unsigned _hash;
+
+ enum { capacityForIdentifier = 0x10000000 };
+
+ static Rep null;
+ static Rep empty;
+ };
+
+ public:
+ /**
+ * Constructs a null string.
+ */
+ UString();
+ /**
+ * Constructs a string from the single character c.
+ */
+ explicit UString(char c);
+ /**
+ * Constructs a string from a classical zero determined 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) { attach(s.rep); }
+ /**
+ * Convenience declaration only ! You'll be on your own to write the
+ * implementation for a construction from QString.
+ *
+ * Note: feel free to contact me if you want to see a dummy header for
+ * your favorite FooString class here !
+ */
+ UString(const QString &);
+ /**
+ * Convenience declaration only ! See UString(const QString&).
+ */
+ UString(const DOM::DOMString &);
+ /**
+ * Concatenation constructor. Makes operator+ more efficient.
+ */
+ UString(const UString &, const UString &);
+ /**
+ * Destructor. If this handle was the only one holding a reference to the
+ * string the data will be freed.
+ */
+ ~UString() { release(); }
+
+ /**
+ * 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.
+ */
+ static UString from(long l);
+ /**
+ * Constructs a string from a double.
+ */
+ static UString from(double d);
+
+ /**
+ * Append another string.
+ */
+ UString &append(const UString &);
+
+ /**
+ * @return The string converted to the 8-bit string type CString().
+ */
+ 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;
+ /**
+ * @see UString(const QString&).
+ */
+ DOM::DOMString string() const;
+ /**
+ * @see UString(const QString&).
+ */
+ QString qstring() const;
+ /**
+ * @see UString(const QString&).
+ */
+ QConstString qconststring() const;
+
+ /**
+ * Assignment operator.
+ */
+ UString &operator=(const char *c);
+ UString &operator=(const UString &);
+ /**
+ * Appends the specified string.
+ */
+ UString &operator+=(const UString &s) { return append(s); }
+
+ /**
+ * @return A pointer to the internal Unicode data.
+ */
+ const UChar* data() const { return rep->data(); }
+ /**
+ * @return True if null.
+ */
+ bool isNull() const { return (rep == &Rep::null); }
+ /**
+ * @return True if null or zero length.
+ */
+ bool isEmpty() const { return (!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 rep->size(); }
+ /**
+ * Const character at specified position.
+ */
+ UChar operator[](int pos) const;
+ /**
+ * Writable reference to character at specified position.
+ */
+ UCharReference operator[](int pos);
+
+ /**
+ * 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 an unsigned long integer. ok will be set
+ * according to the success.
+ @ @param ok make this point to a bool in case you need to know whether the conversion succeeded.
+ * @param tolerateEmptyString if false, toULong will return false for *ok for an empty string.
+ */
+ unsigned long toULong(bool *ok, bool tolerateEmptyString) const;
+ unsigned long toULong(bool *ok = 0) const;
+
+ unsigned int toUInt32(bool *ok = 0) const;
+ unsigned int 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;
+
+ /**
+ * Returns this string converted to lower case characters
+ */
+ UString toLower() const;
+ /**
+ * Returns this string converted to upper case characters
+ */
+ UString toUpper() 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 UString null;
+#ifdef KJS_DEBUG_MEM
+ /**
+ * Clear statically allocated resources.
+ */
+ static void globalClear();
+#endif
+ private:
+ UString(Rep *r) { attach(r); }
+ void attach(Rep *r);
+ void detach();
+ void release();
+ Rep *rep;
+ };
+
+ KJS_EXPORT inline bool operator==(const UChar &c1, const UChar &c2) {
+ return (c1.uc == c2.uc);
+ }
+ KJS_EXPORT inline bool operator!=(const UChar& c1, const UChar& c2) {
+ return !KJS::operator==(c1, c2);
+ }
+ KJS_EXPORT bool operator==(const UString& s1, const UString& s2);
+ inline bool operator!=(const UString& s1, const UString& s2) {
+ return !KJS::operator==(s1, s2);
+ }
+ KJS_EXPORT bool operator<(const UString& s1, const UString& s2);
+ KJS_EXPORT bool operator==(const UString& s1, const char *s2);
+ KJS_EXPORT inline bool operator!=(const UString& s1, const char *s2) {
+ return !KJS::operator==(s1, s2);
+ }
+ KJS_EXPORT inline bool operator==(const char *s1, const UString& s2) {
+ return operator==(s2, s1);
+ }
+ KJS_EXPORT inline bool operator!=(const char *s1, const UString& s2) {
+ return !KJS::operator==(s1, s2);
+ }
+ KJS_EXPORT bool operator==(const CString& s1, const CString& s2);
+ KJS_EXPORT inline bool operator!=(const CString& s1, const CString& s2) {
+ return !KJS::operator==(s1, s2);
+ }
+ KJS_EXPORT inline UString operator+(const UString& s1, const UString& s2) {
+ return UString(s1, s2);
+ }
+
+ KJS_EXPORT int compare(const UString &, const UString &);
+
+} // namespace
+
+#endif
diff --git a/kjs/value.cpp b/kjs/value.cpp
new file mode 100644
index 000000000..e95756c48
--- /dev/null
+++ b/kjs/value.cpp
@@ -0,0 +1,412 @@
+// -*- 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 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 "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "internal.h"
+#include "collector.h"
+#include "operations.h"
+#include "error_object.h"
+#include "nodes.h"
+#include "simple_number.h"
+
+using namespace KJS;
+
+// ----------------------------- ValueImp -------------------------------------
+
+ValueImp::ValueImp() :
+ refcount(0),
+ // Tell the garbage collector that this memory block corresponds to a real object now
+ _flags(VI_CREATED)
+{
+ //fprintf(stderr,"ValueImp::ValueImp %p\n",(void*)this);
+}
+
+ValueImp::~ValueImp()
+{
+ //fprintf(stderr,"ValueImp::~ValueImp %p\n",(void*)this);
+ _flags |= VI_DESTRUCTED;
+}
+
+void ValueImp::mark()
+{
+ //fprintf(stderr,"ValueImp::mark %p\n",(void*)this);
+ _flags |= VI_MARKED;
+}
+
+bool ValueImp::marked() const
+{
+ // Simple numbers are always considered marked.
+ return SimpleNumber::is(this) || (_flags & VI_MARKED);
+}
+
+void ValueImp::setGcAllowed()
+{
+ //fprintf(stderr,"ValueImp::setGcAllowed %p\n",(void*)this);
+ // simple numbers are never seen by the collector so setting this
+ // flag is irrelevant
+ if (!SimpleNumber::is(this))
+ _flags |= VI_GCALLOWED;
+}
+
+void* ValueImp::operator new(size_t s)
+{
+ return Collector::allocate(s);
+}
+
+void ValueImp::operator delete(void*)
+{
+ // Do nothing. So far.
+}
+
+bool ValueImp::toUInt32(unsigned&) const
+{
+ return false;
+}
+
+// ECMA 9.4
+int ValueImp::toInteger(ExecState *exec) const
+{
+ unsigned i;
+ if (dispatchToUInt32(i))
+ return static_cast<int>(i);
+ double d = roundValue(exec, Value(const_cast<ValueImp*>(this)));
+ if (isInf(d))
+ return INT_MAX;
+ return static_cast<int>(d);
+}
+
+int ValueImp::toInt32(ExecState *exec) const
+{
+ unsigned i;
+ if (dispatchToUInt32(i))
+ return (int)i;
+
+ double d = roundValue(exec, Value(const_cast<ValueImp*>(this)));
+ if (isNaN(d) || isInf(d) || d == 0.0)
+ return 0;
+ double d32 = fmod(d, D32);
+
+ //Make sure we use the positive remainder. This matters since this may be
+ //less than MIN_INT (but still < 2^32), and we don't want the cast to clamp.
+ if (d32 < 0)
+ d32 += D32;
+
+ if (d32 >= D32 / 2.0)
+ d32 -= D32;
+
+ return static_cast<int>(d32);
+}
+
+unsigned int ValueImp::toUInt32(ExecState *exec) const
+{
+ unsigned i;
+ if (dispatchToUInt32(i))
+ return i;
+
+ double d = roundValue(exec, Value(const_cast<ValueImp*>(this)));
+ if (isNaN(d) || isInf(d) || d == 0.0)
+ return 0;
+ double d32 = fmod(d, D32);
+
+ if (d32 < 0)
+ d32 += D32;
+
+ //6.3.1.4 Real floating and integer
+ // 50) The remaindering operation performed when a value of integer type is
+ // converted to unsigned type need not be performed when a value of real
+ // floating type is converted to unsigned type. Thus, the range of
+ // portable real floating values is (-1, Utype_MAX+1).
+ return static_cast<unsigned int>(d32);
+}
+
+unsigned short ValueImp::toUInt16(ExecState *exec) const
+{
+ unsigned i;
+ if (dispatchToUInt32(i))
+ return (unsigned short)i;
+
+ double d = roundValue(exec, Value(const_cast<ValueImp*>(this)));
+ double d16 = fmod(d, D16);
+
+ // look at toUInt32 to see why this is necesary
+ int t_int = static_cast<int>(d16);
+ return static_cast<unsigned short>(t_int);
+}
+
+// Dispatchers for virtual functions, to special-case simple numbers which
+// won't be real pointers.
+
+Type ValueImp::dispatchType() const
+{
+ if (SimpleNumber::is(this))
+ return NumberType;
+ return type();
+}
+
+Value ValueImp::dispatchToPrimitive(ExecState *exec, Type preferredType) const
+{
+ if (SimpleNumber::is(this))
+ return Value(const_cast<ValueImp *>(this));
+ return toPrimitive(exec, preferredType);
+}
+
+bool ValueImp::dispatchToBoolean(ExecState *exec) const
+{
+ if (SimpleNumber::is(this))
+ return SimpleNumber::value(this);
+ return toBoolean(exec);
+}
+
+double ValueImp::dispatchToNumber(ExecState *exec) const
+{
+ if (SimpleNumber::is(this))
+ return SimpleNumber::value(this);
+ return toNumber(exec);
+}
+
+UString ValueImp::dispatchToString(ExecState *exec) const
+{
+ if (SimpleNumber::is(this))
+ return UString::from(SimpleNumber::value(this));
+ return toString(exec);
+}
+
+Object ValueImp::dispatchToObject(ExecState *exec) const
+{
+ if (SimpleNumber::is(this))
+ return static_cast<const NumberImp *>(this)->NumberImp::toObject(exec);
+ return toObject(exec);
+}
+
+bool ValueImp::dispatchToUInt32(unsigned& result) const
+{
+ if (SimpleNumber::is(this)) {
+ long i = SimpleNumber::value(this);
+ if (i < 0)
+ return false;
+ result = (unsigned)i;
+ return true;
+ }
+ return toUInt32(result);
+}
+
+// ------------------------------ Value ----------------------------------------
+
+Value::Value(ValueImp *v)
+{
+ rep = v;
+#ifdef DEBUG_COLLECTOR
+ assert (!(rep && !SimpleNumber::is(rep) && *((uint32_t *)rep) == 0 ));
+ assert (!(rep && !SimpleNumber::is(rep) && rep->_flags & ValueImp::VI_MARKED));
+#endif
+ if (v)
+ {
+ v->ref();
+ //fprintf(stderr, "Value::Value(%p) imp=%p ref=%d\n", this, rep, rep->refcount);
+ v->setGcAllowed();
+ }
+}
+
+Value::Value(const Value &v)
+{
+ rep = v.imp();
+#ifdef DEBUG_COLLECTOR
+ assert (!(rep && !SimpleNumber::is(rep) && *((uint32_t *)rep) == 0 ));
+ assert (!(rep && !SimpleNumber::is(rep) && rep->_flags & ValueImp::VI_MARKED));
+#endif
+ if (rep)
+ {
+ rep->ref();
+ //fprintf(stderr, "Value::Value(%p)(copying %p) imp=%p ref=%d\n", this, &v, rep, rep->refcount);
+ }
+}
+
+Value::~Value()
+{
+ if (rep)
+ {
+ rep->deref();
+ //fprintf(stderr, "Value::~Value(%p) imp=%p ref=%d\n", this, rep, rep->refcount);
+ }
+}
+
+Value& Value::operator=(const Value &v)
+{
+ ValueImp *tmpRep = v.imp();
+
+ //Avoid the destruction of the object underneath us by
+ //incrementing the reference on it first
+ if (tmpRep) {
+ tmpRep->ref();
+ //fprintf(stderr, "Value::operator=(%p)(copying %p) imp=%p ref=%d\n", this, &v, tmpRep, tmpRep->refcount);
+ }
+
+ if (rep) {
+ rep->deref();
+ //fprintf(stderr, "Value::operator=(%p)(copying %p) old imp=%p ref=%d\n", this, &v, rep, rep->refcount);
+ }
+ rep = tmpRep;
+
+ return *this;
+}
+
+// ------------------------------ Undefined ------------------------------------
+
+Undefined::Undefined() : Value(UndefinedImp::staticUndefined)
+{
+}
+
+Undefined Undefined::dynamicCast(const Value &v)
+{
+ if (!v.isValid() || v.type() != UndefinedType)
+ return Undefined(0);
+
+ return Undefined();
+}
+
+// ------------------------------ Null -----------------------------------------
+
+Null::Null() : Value(NullImp::staticNull)
+{
+}
+
+Null Null::dynamicCast(const Value &v)
+{
+ if (!v.isValid() || v.type() != NullType)
+ return Null(0);
+
+ return Null();
+}
+
+// ------------------------------ Boolean --------------------------------------
+
+Boolean::Boolean(bool b)
+ : Value(b ? BooleanImp::staticTrue : BooleanImp::staticFalse)
+{
+}
+
+bool Boolean::value() const
+{
+ assert(rep);
+ return ((BooleanImp*)rep)->value();
+}
+
+Boolean Boolean::dynamicCast(const Value &v)
+{
+ if (!v.isValid() || v.type() != BooleanType)
+ return static_cast<BooleanImp*>(0);
+
+ return static_cast<BooleanImp*>(v.imp());
+}
+
+// ------------------------------ String ---------------------------------------
+
+String::String(const UString &s) : Value(new StringImp(s))
+{
+#ifndef NDEBUG
+ if (s.isNull())
+ fprintf(stderr, "WARNING: KJS::String constructed from null string\n");
+#endif
+}
+
+UString String::value() const
+{
+ assert(rep);
+ return ((StringImp*)rep)->value();
+}
+
+String String::dynamicCast(const Value &v)
+{
+ if (!v.isValid() || v.type() != StringType)
+ return String(0);
+
+ return String(static_cast<StringImp*>(v.imp()));
+}
+
+// ------------------------------ Number ---------------------------------------
+
+Number::Number(int i)
+ : Value(SimpleNumber::fits(i) ? SimpleNumber::make(i) : new NumberImp(static_cast<double>(i))) { }
+
+Number::Number(unsigned int u)
+ : Value(SimpleNumber::fits(u) ? SimpleNumber::make(u) : new NumberImp(static_cast<double>(u))) { }
+
+Number::Number(double d)
+#if defined(__alpha) && !defined(_IEEE_FP)
+ // check for NaN first if we werent't compiled with -mieee on Alpha
+ : Value(KJS::isNaN(d) ? NumberImp::staticNaN : (SimpleNumber::fits(d) ? SimpleNumber::make((long)d) : new NumberImp(d))) { }
+#else
+ : Value(SimpleNumber::fits(d) ? SimpleNumber::make((long)d) : (KJS::isNaN(d) ? NumberImp::staticNaN : new NumberImp(d))) { }
+#endif
+
+Number::Number(long int l)
+ : Value(SimpleNumber::fits(l) ? SimpleNumber::make(l) : new NumberImp(static_cast<double>(l))) { }
+
+Number::Number(long unsigned int l)
+ : Value(SimpleNumber::fits(l) ? SimpleNumber::make(l) : new NumberImp(static_cast<double>(l))) { }
+
+Number Number::dynamicCast(const Value &v)
+{
+ if (!v.isValid() || v.type() != NumberType)
+ return Number((NumberImp*)0);
+
+ return Number(static_cast<NumberImp*>(v.imp()));
+}
+
+double Number::value() const
+{
+ if (SimpleNumber::is(rep))
+ return (double)SimpleNumber::value(rep);
+ assert(rep);
+ return ((NumberImp*)rep)->value();
+}
+
+int Number::intValue() const
+{
+ if (SimpleNumber::is(rep))
+ return SimpleNumber::value(rep);
+ return (int)((NumberImp*)rep)->value();
+}
+
+bool Number::isNaN() const
+{
+ return rep == NumberImp::staticNaN;
+}
+
+bool Number::isInf() const
+{
+ if (SimpleNumber::is(rep))
+ return false;
+ return KJS::isInf(((NumberImp*)rep)->value());
+}
diff --git a/kjs/value.h b/kjs/value.h
new file mode 100644
index 000000000..7c2e4b111
--- /dev/null
+++ b/kjs/value.h
@@ -0,0 +1,400 @@
+// -*- 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 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_VALUE_H_
+#define _KJS_VALUE_H_
+
+#include <stdlib.h> // Needed for size_t
+
+#include "ustring.h"
+#include "simple_number.h"
+
+// Primitive data types
+
+namespace KJS {
+
+ class Value;
+ class ValueImp;
+ class ValueImpPrivate;
+ class Undefined;
+ class UndefinedImp;
+ class Null;
+ class NullImp;
+ class Boolean;
+ class BooleanImp;
+ class String;
+ class StringImp;
+ class Number;
+ class NumberImp;
+ class Object;
+ class ObjectImp;
+ class Reference;
+ class List;
+ class ListImp;
+ class Completion;
+ class ExecState;
+
+ /**
+ * Primitive types
+ */
+ enum Type {
+ UnspecifiedType = 0,
+ UndefinedType = 1,
+ NullType = 2,
+ BooleanType = 3,
+ StringType = 4,
+ NumberType = 5,
+ ObjectType = 6
+ };
+
+ /**
+ * ValueImp is the base type for all primitives (Undefined, Null, Boolean,
+ * String, Number) and objects in ECMAScript.
+ *
+ * Note: you should never inherit from ValueImp as it is for primitive types
+ * only (all of which are provided internally by KJS). Instead, inherit from
+ * ObjectImp.
+ */
+ class KJS_EXPORT ValueImp {
+ friend class Collector;
+ friend class Value;
+ friend class ContextImp;
+ public:
+ ValueImp();
+ virtual ~ValueImp();
+
+ ValueImp* ref() { if (!SimpleNumber::is(this)) refcount++; return this; }
+ bool deref() { if (SimpleNumber::is(this)) return false; else return (!--refcount); }
+
+ virtual void mark();
+ bool marked() const;
+ void* operator new(size_t);
+ void operator delete(void*);
+
+ /**
+ * @internal
+ *
+ * set by Object() so that the collector is allowed to delete us
+ */
+ void setGcAllowed();
+
+ // Will crash if called on a simple number.
+ void setGcAllowedFast() { _flags |= VI_GCALLOWED; }
+
+ int toInteger(ExecState *exec) const;
+ int toInt32(ExecState *exec) const;
+ unsigned int toUInt32(ExecState *exec) const;
+ unsigned short toUInt16(ExecState *exec) const;
+
+ // Dispatch wrappers that handle the special small number case
+
+ Type dispatchType() const;
+ Value dispatchToPrimitive(ExecState *exec, Type preferredType = UnspecifiedType) const;
+ bool dispatchToBoolean(ExecState *exec) const;
+ double dispatchToNumber(ExecState *exec) const;
+ UString dispatchToString(ExecState *exec) const;
+ bool dispatchToUInt32(unsigned&) const;
+ Object dispatchToObject(ExecState *exec) const;
+
+ unsigned short int refcount;
+
+ bool isDestroyed() const { return _flags & VI_DESTRUCTED; }
+
+ private:
+ unsigned short int _flags;
+
+ virtual Type type() const = 0;
+
+ // The conversion operations
+
+ virtual Value toPrimitive(ExecState *exec, Type preferredType = UnspecifiedType) const = 0;
+ virtual bool toBoolean(ExecState *exec) const = 0;
+ virtual double toNumber(ExecState *exec) const = 0;
+ // TODO: no need for the following 4 int conversions to be virtual
+ virtual UString toString(ExecState *exec) const = 0;
+ virtual Object toObject(ExecState *exec) const = 0;
+ virtual bool toUInt32(unsigned&) const;
+
+ enum {
+ VI_MARKED = 1,
+ VI_GCALLOWED = 2,
+ VI_CREATED = 4,
+ VI_DESTRUCTED = 8 // nice word we have here :)
+ }; // VI means VALUEIMPL
+
+ ValueImpPrivate *_vd;
+
+ // Give a compile time error if we try to copy one of these.
+ ValueImp(const ValueImp&);
+ ValueImp& operator=(const ValueImp&);
+ };
+
+ /**
+ * Value objects are act as wrappers ("smart pointers") around ValueImp
+ * objects and their descendents. Instead of using ValueImps
+ * (and derivatives) during normal program execution, you should use a
+ * Value-derived class.
+ *
+ * Value maintains a pointer to a ValueImp object and uses a reference
+ * counting scheme to ensure that the ValueImp object is not deleted or
+ * garbage collected.
+ *
+ * Note: The conversion operations all return values of various types -
+ * if an error occurs during conversion, an error object will instead
+ * be returned (where possible), and the execution state's exception
+ * will be set appropriately.
+ */
+ class KJS_EXPORT Value {
+ public:
+ Value() : rep(0) { }
+ explicit Value(ValueImp *v);
+ Value(const Value &v);
+ ~Value();
+
+ Value& operator=(const Value &v);
+ /**
+ * Returns whether or not this is a valid value. An invalid value
+ * has a 0 implementation pointer and should not be used for
+ * any other operation than this check. Current use: as a
+ * distinct return value signalling failing dynamicCast() calls.
+ */
+ bool isValid() const { return rep != 0; }
+ /**
+ * @deprecated
+ * Use !isValid() instead.
+ */
+ bool isNull() const { return rep == 0; }
+ ValueImp *imp() const { return rep; }
+
+ /**
+ * Returns the type of value. This is one of UndefinedType, NullType,
+ * BooleanType, StringType, NumberType, or ObjectType.
+ *
+ * @return The type of value
+ */
+ Type type() const { return rep->dispatchType(); }
+
+ /**
+ * Checks whether or not the value is of a particular tpye
+ *
+ * @param t The type to compare with
+ * @return true if the value is of the specified type, otherwise false
+ */
+ bool isA(Type t) const { return rep->dispatchType() == t; }
+
+ /**
+ * Performs the ToPrimitive type conversion operation on this value
+ * (ECMA 9.1)
+ */
+ Value toPrimitive(ExecState *exec,
+ Type preferredType = UnspecifiedType) const
+ { return rep->dispatchToPrimitive(exec, preferredType); }
+
+ /**
+ * Performs the ToBoolean type conversion operation on this value (ECMA 9.2)
+ */
+ bool toBoolean(ExecState *exec) const { return rep->dispatchToBoolean(exec); }
+
+ /**
+ * Performs the ToNumber type conversion operation on this value (ECMA 9.3)
+ */
+ double toNumber(ExecState *exec) const { return rep->dispatchToNumber(exec); }
+
+ /**
+ * Performs the ToInteger type conversion operation on this value (ECMA 9.4)
+ */
+ int toInteger(ExecState *exec) const { return rep->toInteger(exec); }
+
+ /**
+ * Performs the ToInt32 type conversion operation on this value (ECMA 9.5)
+ */
+ int toInt32(ExecState *exec) const { return rep->toInt32(exec); }
+
+ /**
+ * Performs the ToUInt32 type conversion operation on this value (ECMA 9.6)
+ */
+ unsigned int toUInt32(ExecState *exec) const { return rep->toUInt32(exec); }
+
+ /**
+ * Performs the ToUInt16 type conversion operation on this value (ECMA 9.7)
+ */
+ unsigned short toUInt16(ExecState *exec) const { return rep->toUInt16(exec); }
+
+ /**
+ * Performs the ToString type conversion operation on this value (ECMA 9.8)
+ */
+ UString toString(ExecState *exec) const { return rep->dispatchToString(exec); }
+
+ /**
+ * Performs the ToObject type conversion operation on this value (ECMA 9.9)
+ */
+ Object toObject(ExecState *exec) const;
+
+ /**
+ * Checks if we can do a lossless conversion to UInt32.
+ */
+ bool toUInt32(unsigned& i) const { return rep->dispatchToUInt32(i); }
+
+ protected:
+ ValueImp *rep;
+ };
+
+ // Primitive types
+
+ /**
+ * Represents an primitive Undefined value. All instances of this class
+ * share the same implementation object, so == will always return true
+ * for any comparison between two Undefined objects.
+ */
+ class KJS_EXPORT Undefined : public Value {
+ public:
+ Undefined();
+
+ /**
+ * Converts a Value into an Undefined. If the value's type is not
+ * UndefinedType, a null object will be returned (i.e. one with it's
+ * internal pointer set to 0). If you do not know for sure whether the
+ * value is of type UndefinedType, you should check the isValid()
+ * methods afterwards before calling any methods on the returned value.
+ *
+ * @return The value converted to an Undefined
+ */
+ static Undefined dynamicCast(const Value &v);
+ private:
+ friend class UndefinedImp;
+ explicit Undefined(UndefinedImp *v);
+
+ };
+
+ /**
+ * Represents an primitive Null value. All instances of this class
+ * share the same implementation object, so == will always return true
+ * for any comparison between two Null objects.
+ */
+ class KJS_EXPORT Null : public Value {
+ public:
+ Null();
+
+ /**
+ * Converts a Value into an Null. If the value's type is not NullType,
+ * a null object will be returned (i.e. one with it's internal pointer set
+ * to 0). If you do not know for sure whether the value is of type
+ * NullType, you should check the isValid() methods afterwards before
+ * calling any methods on the returned value.
+ *
+ * @return The value converted to a Null
+ */
+ static Null dynamicCast(const Value &v);
+ private:
+ friend class NullImp;
+ explicit Null(NullImp *v);
+ };
+
+ /**
+ * Represents an primitive Boolean value
+ */
+ class KJS_EXPORT Boolean : public Value {
+ public:
+ Boolean(bool b = false);
+
+ /**
+ * Converts a Value into an Boolean. If the value's type is not BooleanType,
+ * a null object will be returned (i.e. one with it's internal pointer set
+ * to 0). If you do not know for sure whether the value is of type
+ * BooleanType, you should check the isValid() methods afterwards before
+ * calling any methods on the returned value.
+ *
+ * @return The value converted to a Boolean
+ */
+ static Boolean dynamicCast(const Value &v);
+
+ bool value() const;
+ private:
+ friend class BooleanImp;
+ explicit Boolean(BooleanImp *v);
+ };
+
+ /**
+ * Represents an primitive String value
+ */
+ class KJS_EXPORT String : public Value {
+ public:
+ String(const UString &s = "");
+
+ /**
+ * Converts a Value into an String. If the value's type is not StringType,
+ * a null object will be returned (i.e. one with it's internal pointer set
+ * to 0). If you do not know for sure whether the value is of type
+ * StringType, you should check the isValid() methods afterwards before
+ * calling any methods on the returned value.
+ *
+ * @return The value converted to a String
+ */
+ static String dynamicCast(const Value &v);
+
+ UString value() const;
+ private:
+ friend class StringImp;
+ explicit String(StringImp *v);
+ };
+
+ extern const double NaN;
+ extern const double Inf;
+
+ /**
+ * Represents an primitive Number value
+ */
+ class KJS_EXPORT Number : public Value {
+ friend class ValueImp;
+ public:
+ Number(int i);
+ Number(unsigned int u);
+ Number(double d = 0.0);
+ Number(long int l);
+ Number(long unsigned int l);
+
+ double value() const;
+ int intValue() const;
+
+ bool isNaN() const;
+ bool isInf() const;
+
+ /**
+ * Converts a Value into an Number. If the value's type is not NumberType,
+ * a null object will be returned (i.e. one with it's internal pointer set
+ * to 0). If you do not know for sure whether the value is of type
+ * NumberType, you should check the isNull() methods afterwards before
+ * calling any methods on the returned value.
+ *
+ * @return The value converted to a Number
+ */
+ static Number dynamicCast(const Value &v);
+ private:
+ friend class NumberImp;
+ explicit Number(NumberImp *v);
+ };
+
+} // namespace
+
+#endif // _KJS_VALUE_H_