diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /kjs | |
download | tdelibs-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')
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 = ¶m; + 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 & 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_ |