libkjs_o_LDFLAGS = -Wl,-r -nostdlib
libkjs_o_AR = $(OBJCXXLD) $(AM_OBJCXXFLAGS) $(OBJCXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) $(libkjs_o_LDFLAGS) -o
+INCLUDES = $(KWQ_INCLUDES)
+
libkjs_o_SOURCES = \
array_object.cpp \
array_object.h \
function.h \
function_object.cpp \
function_object.h \
- global_object.cpp \
grammar.cpp \
grammar.h \
internal.cpp \
internal.h \
- kjs.cpp \
- kjs.h \
+ interpreter.cpp \
+ interpreter.h \
lexer.cpp \
lexer.h \
lexer.lut.h \
object_object.h \
operations.cpp \
operations.h \
+ property_map.cpp \
+ property_map.h \
regexp.cpp \
regexp.h \
regexp_object.cpp \
types.h \
ustring.cpp \
ustring.h \
+ value.cpp \
+ value.h \
$(NULL)
noinst_PROGRAMS = testkjs
ln -sf grammar.cpp.h grammar.h
touch ./grammar-stamp
-BUILT_SOURCES = $(GRAMMAR_FILES) grammar-stamp
+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
+
+lexer.lut.h: keywords.table
+ ./create_hash_table keywords.table -i > lexer.lut.h;
+
+array_object.lut.h: array_object.cpp
+ ./create_hash_table array_object.cpp -i > array_object.lut.h
+
+math_object.lut.h: math_object.cpp
+ ./create_hash_table math_object.cpp -i > math_object.lut.h
+
+date_object.lut.h: date_object.cpp
+ ./create_hash_table date_object.cpp -i > date_object.lut.h
+
+number_object.lut.h: number_object.cpp
+ ./create_hash_table number_object.cpp -i > number_object.lut.h
+
+string_object.lut.h: string_object.cpp
+ ./create_hash_table string_object.cpp -i > string_object.lut.h
+
+
+
+BUILT_SOURCES = $(GRAMMAR_FILES) $(LUT_FILES) grammar-stamp
CLEANFILES = $(BUILT_SOURCES)
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
*/
-#include "kjs.h"
-#include "operations.h"
+#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 <assert.h>
using namespace KJS;
-ArrayObject::ArrayObject(const Object &funcProto,
- const Object &arrayProto)
- : ConstructorImp(funcProto, 1)
+// ------------------------------ ArrayInstanceImp -----------------------------
+
+const ClassInfo ArrayInstanceImp::info = {"Array", 0, 0, 0};
+
+ArrayInstanceImp::ArrayInstanceImp(const Object &proto)
+ : ObjectImp(proto)
{
- // ECMA 15.4.3.1 Array.prototype
- setPrototypeProperty(arrayProto);
}
-// ECMA 15.6.1
-Completion ArrayObject::execute(const List &args)
+// Special implementation of [[Put]] - see ECMA 15.4.5.1
+void ArrayInstanceImp::put(ExecState *exec, const UString &propertyName, const Value &value, int attr)
{
- // equivalent to 'new Array(....)'
- KJSO result = construct(args);
+ if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName))
+ return;
+
+ if (hasProperty(exec,propertyName)) {
+ if (propertyName == "length") {
+ Value len = get(exec,"length");
+ unsigned int oldLen = len.toUInt32(exec);
+ unsigned int newLen = value.toUInt32(exec);
+ // shrink array
+ for (unsigned int u = newLen; u < oldLen; u++) {
+ UString p = UString::from(u);
+ if (hasProperty(exec, p, false))
+ deleteProperty(exec, p);
+ }
+ ObjectImp::put(exec, "length", Number(newLen), DontEnum | DontDelete);
+ return;
+ }
+ // put(p, v);
+ } // } else
+ ObjectImp::put(exec, propertyName, value, attr);
+
+ // array index ?
+ unsigned int idx;
+ if (!sscanf(propertyName.cstring().c_str(), "%u", &idx)) /* TODO */
+ return;
- return Completion(ReturnValue, result);
+ // do we need to update/create the length property ?
+ if (hasProperty(exec, "length", false)) {
+ Value len = get(exec, "length");
+ if (idx < len.toUInt32(exec))
+ return;
+ }
+
+ ObjectImp::put(exec, "length", Number(idx+1), DontDelete | DontEnum);
}
-// ECMA 15.6.2
-Object ArrayObject::construct(const List &args)
+void ArrayInstanceImp::putDirect(ExecState *exec, const UString &propertyName, const Value &value, int attr)
{
- Object result = Object::create(ArrayClass);
+ ObjectImp::put(exec,propertyName,value,attr);
+}
+// ------------------------------ ArrayPrototypeImp ----------------------------
- unsigned int len;
- ListIterator it = args.begin();
- // a single argument might denote the array size
- if (args.size() == 1 && it->isA(NumberType))
- len = it->toUInt32();
- else {
- // initialize array
- len = args.size();
- for (unsigned int u = 0; it != args.end(); it++, u++)
- result.put(UString::from(u), *it);
- }
+const ClassInfo ArrayPrototypeImp::info = {"Array", &ArrayInstanceImp::info, &arrayTable, 0};
- // array size
- result.put("length", len, DontEnum | DontDelete);
+/* Source for array_object.lut.h
+@begin arrayTable 13
+ 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
+*/
- return result;
+// ECMA 15.4.4
+ArrayPrototypeImp::ArrayPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto)
+ : ArrayInstanceImp(Object(objProto))
+{
+ Value protect(this);
+ setInternalValue(Null());
+
+ // The constructor will be added later, by InterpreterImp, once ArrayObjectImp has been constructed.
+ put(exec,"length", Number(0), DontEnum | DontDelete);
}
-// ECMA 15.6.4
-ArrayPrototype::ArrayPrototype(const Object& proto)
- : ObjectImp(ArrayClass, Null(), proto)
+Value ArrayPrototypeImp::get(ExecState *exec, const UString &propertyName) const
{
- // The constructor will be added later in ArrayObject's constructor
+ //fprintf( stderr, "ArrayPrototypeImp::get(%s)\n", propertyName.ascii() );
+ return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this );
+}
+
+// ------------------------------ ArrayProtoFuncImp ----------------------------
- put("length", 0u, DontEnum | DontDelete);
+ArrayProtoFuncImp::ArrayProtoFuncImp(ExecState *exec, int i, int len)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
+ ), id(i)
+{
+ Value protect(this);
+ put(exec,"length",Number(len),DontDelete|ReadOnly|DontEnum);
}
-KJSO ArrayPrototype::get(const UString &p) const
+bool ArrayProtoFuncImp::implementsCall() const
{
- int id;
- if(p == "toString")
- id = ArrayProtoFunc::ToString;
- else if(p == "toLocaleString")
- id = ArrayProtoFunc::ToLocaleString;
- else if(p == "concat")
- id = ArrayProtoFunc::Concat;
- else if (p == "join")
- id = ArrayProtoFunc::Join;
- else if(p == "pop")
- id = ArrayProtoFunc::Pop;
- else if(p == "push")
- id = ArrayProtoFunc::Push;
- else if(p == "reverse")
- id = ArrayProtoFunc::Reverse;
- else if(p == "shift")
- id = ArrayProtoFunc::Shift;
- else if(p == "slice")
- id = ArrayProtoFunc::Slice;
- else if(p == "sort")
- id = ArrayProtoFunc::Sort;
- else if(p == "splice")
- id = ArrayProtoFunc::Splice;
- else if(p == "unshift")
- id = ArrayProtoFunc::UnShift;
- else
- return Imp::get(p);
-
- return Function(new ArrayProtoFunc(id));
+ return true;
}
// ECMA 15.4.4
-Completion ArrayProtoFunc::execute(const List &args)
+Value ArrayProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
{
- KJSO result, obj, obj2;
- Object thisObj = Object::dynamicCast(thisValue());
- unsigned int length = thisObj.get("length").toUInt32();
- unsigned int middle;
- UString str = "", str2;
- UString separator = ",";
+ unsigned int length = thisObj.get(exec,"length").toUInt32(exec);
+ Value result;
switch (id) {
case ToLocaleString:
- /* TODO */
- // fall trough
+ // TODO - see 15.4.4.3
+ // fall through
case ToString:
- if (!thisObj.getClass() == ArrayClass) {
- result = Error::create(TypeError);
- break;
+
+ if (!thisObj.inherits(&ArrayInstanceImp::info)) {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
}
- // fall trough
- case Join:
- {
- if (!args[0].isA(UndefinedType))
- separator = args[0].toString().value();
- for (unsigned int k = 0; k < length; k++) {
- if (k >= 1)
- str += separator;
- obj = thisObj.get(UString::from(k));
- if (!obj.isA(UndefinedType) && !obj.isA(NullType))
- str += obj.toString().value();
- }
+
+ // fall through
+
+ case Join: {
+ UString separator = ",";
+ UString str = "";
+
+ if (args.size() > 0)
+ separator = args[0].toString(exec);
+ for (unsigned int k = 0; k < length; k++) {
+ if (k >= 1)
+ str += separator;
+ Value element = thisObj.get(exec,UString::from(k));
+ if (element.type() != UndefinedType && element.type() != NullType)
+ str += element.toString(exec);
}
result = String(str);
break;
+ }
case Concat: {
- result = Object::create(ArrayClass);
+ Object arr = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
int n = 0;
- obj = thisObj;
+ Value curArg = thisObj;
+ Object curObj = Object::dynamicCast(thisObj);
ListIterator it = args.begin();
for (;;) {
- if (obj.isA(ObjectType) &&
- static_cast<Object&>(obj).getClass() == ArrayClass) {
- unsigned int k = 0;
- if (n > 0)
- length = obj.get("length").toUInt32();
- while (k < length) {
- UString p = UString::from(k);
- if (obj.hasProperty(p))
- result.put(UString::from(n), obj.get(p));
- n++;
- k++;
- }
+ if (curArg.type() == ObjectType &&
+ curObj.inherits(&ArrayInstanceImp::info)) {
+ unsigned int k = 0;
+ if (n > 0)
+ length = curObj.get(exec,"length").toUInt32(exec);
+ while (k < length) {
+ UString p = UString::from(k);
+ if (curObj.hasProperty(exec,p))
+ arr.put(exec,UString::from(n), curObj.get(exec,p));
+ n++;
+ k++;
+ }
} else {
- result.put(UString::from(n), obj);
- n++;
+ arr.put(exec,UString::from(n), curArg);
+ n++;
}
if (it == args.end())
- break;
- obj = it++;
+ break;
+ curArg = *it;
+ curObj = Object::dynamicCast(it++); // may be 0
}
- result.put("length", Number(n), DontEnum | DontDelete);
- }
+ arr.put(exec,"length", Number(n), DontEnum | DontDelete);
+
+ result = arr;
break;
- case Pop:
+ }
+ case Pop:{
+
if (length == 0) {
- thisObj.put("length", Number(length), DontEnum | DontDelete);
+ thisObj.put(exec, "length", Number(length), DontEnum | DontDelete);
result = Undefined();
} else {
- str = UString::from(length - 1);
- result = thisObj.get(str);
- thisObj.deleteProperty(str);
- thisObj.put("length", length - 1, DontEnum | DontDelete);
+ UString str = UString::from(length - 1);
+ result = thisObj.get(exec,str);
+ thisObj.deleteProperty(exec, str);
+ thisObj.put(exec, "length", Number(length - 1), DontEnum | DontDelete);
}
break;
- case Push:
- {
- for (int n = 0; n < args.size(); n++)
- thisObj.put(UString::from(length + n), args[n]);
- length += args.size();
- thisObj.put("length", length, DontEnum | DontDelete);
- result = Number(length);
- }
+ }
+ case Push: {
+ for (int n = 0; n < args.size(); n++)
+ thisObj.put(exec,UString::from(length + n), args[n]);
+ length += args.size();
+ thisObj.put(exec,"length", Number(length), DontEnum | DontDelete);
+ result = Number(length);
break;
- case Reverse:
- {
- middle = length / 2;
- for (unsigned int k = 0; k < middle; k++) {
- str = UString::from(k);
- str2 = UString::from(length - k - 1);
- obj = thisObj.get(str);
- obj2 = thisObj.get(str2);
- if (thisObj.hasProperty(str2)) {
- if (thisObj.hasProperty(str)) {
- thisObj.put(str, obj2);
- thisObj.put(str2, obj);
- } else {
- thisObj.put(str, obj2);
- thisObj.deleteProperty(str2);
- }
- } else {
- if (thisObj.hasProperty(str)) {
- thisObj.deleteProperty(str);
- thisObj.put(str2, obj);
- } else {
- // why delete something that's not there ? Strange.
- thisObj.deleteProperty(str);
- thisObj.deleteProperty(str2);
- }
- }
+ }
+ case Reverse: {
+
+ unsigned int middle = length / 2;
+
+ for (unsigned int k = 0; k < middle; k++) {
+ UString str = UString::from(k);
+ UString str2 = UString::from(length - k - 1);
+ Value obj = thisObj.get(exec,str);
+ Value obj2 = thisObj.get(exec,str2);
+ if (thisObj.hasProperty(exec,str2)) {
+ if (thisObj.hasProperty(exec,str)) {
+ thisObj.put(exec, str, obj2);
+ thisObj.put(exec, str2, obj);
+ } else {
+ thisObj.put(exec, str, obj2);
+ thisObj.deleteProperty(exec, str2);
+ }
+ } else {
+ if (thisObj.hasProperty(exec, str)) {
+ thisObj.deleteProperty(exec, str);
+ thisObj.put(exec, str2, obj);
+ } else {
+ // why delete something that's not there ? Strange.
+ thisObj.deleteProperty(exec, str);
+ thisObj.deleteProperty(exec, str2);
+ }
}
}
result = thisObj;
break;
- case Shift:
+ }
+ case Shift: {
if (length == 0) {
- thisObj.put("length", Number(length), DontEnum | DontDelete);
+ thisObj.put(exec, "length", Number(length), DontEnum | DontDelete);
result = Undefined();
} else {
- result = thisObj.get("0");
+ result = thisObj.get(exec, "0");
for(unsigned int k = 1; k < length; k++) {
- str = UString::from(k);
- str2 = UString::from(k-1);
- if (thisObj.hasProperty(str)) {
- obj = thisObj.get(str);
- thisObj.put(str2, obj);
- } else
- thisObj.deleteProperty(str2);
+ UString str = UString::from(k);
+ UString str2 = UString::from(k-1);
+ if (thisObj.hasProperty(exec, str)) {
+ Value obj = thisObj.get(exec, str);
+ thisObj.put(exec, str2, obj);
+ } else
+ thisObj.deleteProperty(exec, str2);
}
- thisObj.deleteProperty(UString::from(length - 1));
- thisObj.put("length", length - 1, DontEnum | DontDelete);
+ thisObj.deleteProperty(exec, UString::from(length - 1));
+ thisObj.put(exec, "length", Number(length - 1), DontEnum | DontDelete);
}
break;
- case Slice: // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713
+ }
+ 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->interpreter()->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 );
+ int end = length;
+ if (args[1].type() != UndefinedType)
{
- result = Object::create(ArrayClass); // We return a new array
- int begin = args[0].toUInt32();
- int end = length;
- if (!args[1].isA(UndefinedType))
- {
- end = args[1].toUInt32();
- if ( end < 0 )
- end += length;
- }
- // safety tests
- if ( begin < 0 || end < 0 || begin >= end ) {
- result.put("length", Number(0), DontEnum | DontDelete);
- break;
- }
- //printf( "Slicing from %d to %d \n", begin, end );
- for(unsigned int k = 0; k < (unsigned int) end-begin; k++) {
- str = UString::from(k+begin);
- str2 = UString::from(k);
- if (thisObj.hasProperty(str)) {
- obj = thisObj.get(str);
- result.put(str2, obj);
- }
- }
- result.put("length", end - begin, DontEnum | DontDelete);
- break;
+ end = args[1].toUInt32(exec);
+ if ( end < 0 )
+ end = maxInt( end + length, 0 );
+ else
+ end = minInt( end, length );
}
- case Sort:
- {
+
+ //printf( "Slicing from %d to %d \n", begin, end );
+ for(unsigned int k = 0; k < (unsigned int) end-begin; k++) {
+ UString str = UString::from(k+begin);
+ if (thisObj.hasProperty(exec,str)) {
+ UString str2 = UString::from(k);
+ Value obj = thisObj.get(exec, str);
+ resObj.put(exec, str2, obj);
+ }
+ }
+ resObj.put(exec, "length", Number(end - begin), 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(UString::from(i)).toString().value().ascii() );
+ 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(UString::from(i)).toString().value().ascii() );
#endif
- Object sortFunction;
- bool useSortFunction = !args[0].isA(UndefinedType);
- if (useSortFunction)
- {
- sortFunction = args[0].toObject();
- if (!sortFunction.implementsCall())
- useSortFunction = false;
- }
+ Object sortFunction;
+ bool useSortFunction = (args[0].type() != UndefinedType);
+ if (useSortFunction)
+ {
+ sortFunction = args[0].toObject(exec);
+ if (!sortFunction.implementsCall())
+ useSortFunction = false;
+ }
- if (length == 0) {
- thisObj.put("length", Number(0), DontEnum | DontDelete);
- result = Undefined();
- break;
- }
+ if (length == 0) {
+ thisObj.put(exec, "length", Number(0), DontEnum | DontDelete);
+ result = Undefined();
+ 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 )
- {
- KJSO iObj = thisObj.get(UString::from(i));
- unsigned int themin = i;
- KJSO minObj = iObj;
- for ( unsigned int j = i+1 ; j<length ; ++j )
- {
- KJSO jObj = thisObj.get(UString::from(j));
- int cmp;
- if ( useSortFunction )
- {
- List l;
- l.append(jObj);
- l.append(minObj);
- cmp = sortFunction.executeCall( Global::current(), &l ).toInt32();
- }
- else
- cmp = ( jObj.toString().value() < minObj.toString().value() ) ? -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( UString::from(i), minObj );
- thisObj.put( UString::from(themin), iObj );
- }
- }
+ // "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,UString::from(i));
+ unsigned int themin = i;
+ Value minObj = iObj;
+ for ( unsigned int j = i+1 ; j<length ; ++j )
+ {
+ Value jObj = thisObj.get(exec,UString::from(j));
+ int cmp;
+ if ( useSortFunction )
+ {
+ List l;
+ l.append(jObj);
+ l.append(minObj);
+ Object thisObj = exec->interpreter()->globalObject();
+ cmp = sortFunction.call(exec,thisObj, l ).toInt32(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, UString::from(i), minObj );
+ thisObj.put( exec, UString::from(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(UString::from(i)).toString().value().ascii() );
+ 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(UString::from(i)).toString().value().ascii() );
#endif
- result = thisObj;
- break;
+ result = thisObj;
+ break;
+ }
+ case Splice: {
+ // 15.4.4.12 - oh boy this is huge
+ Object resObj = Object::dynamicCast(exec->interpreter()->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++) {
+ UString str = UString::from(k+begin);
+ if (thisObj.hasProperty(exec,str)) {
+ UString str2 = UString::from(k);
+ Value obj = thisObj.get(exec, str);
+ resObj.put(exec, str2, obj);
+ }
+ }
+ resObj.put(exec, "length", 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 )
+ {
+ UString str = UString::from(k+deleteCount);
+ UString str2 = UString::from(k+additionalArgs);
+ if (thisObj.hasProperty(exec,str)) {
+ Value obj = thisObj.get(exec, str);
+ thisObj.put(exec, str2, obj);
+ }
+ else
+ thisObj.deleteProperty(exec, str2);
+ }
+ for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
+ thisObj.deleteProperty(exec, UString::from(k-1));
+ }
+ else
+ {
+ for ( unsigned int k = length - deleteCount; (int)k > begin; --k )
+ {
+ UString str = UString::from(k+deleteCount-1);
+ UString str2 = UString::from(k+additionalArgs-1);
+ if (thisObj.hasProperty(exec,str)) {
+ Value obj = thisObj.get(exec, str);
+ thisObj.put(exec, str2, obj);
+ }
+ else
+ thisObj.deleteProperty(exec, str2);
+ }
+ }
+ }
+ for ( unsigned int k = 0; k < additionalArgs; ++k )
+ {
+ thisObj.put(exec, UString::from(k+begin), args[k+2]);
}
- // TODO Splice
- // TODO Unshift
+ thisObj.put(exec, "length", 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 )
+ {
+ UString str = UString::from(k-1);
+ UString str2 = UString::from(k+nrArgs-1);
+ if (thisObj.hasProperty(exec,str)) {
+ Value obj = thisObj.get(exec, str);
+ thisObj.put(exec, str2, obj);
+ } else {
+ thisObj.deleteProperty(exec, str2);
+ }
+ }
+ for ( unsigned int k = 0; k < nrArgs; ++k )
+ thisObj.put(exec, UString::from(k), args[k]);
+ result = Number(length + nrArgs);
+ thisObj.put(exec, "length", result, DontEnum | DontDelete);
+ break;
+ }
default:
- result = Undefined();
+ 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,"prototype", Object(arrayProto), DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ put(exec,"length", Number(1), ReadOnly|DontDelete|DontEnum);
+}
- return Completion(ReturnValue, result);
+bool ArrayObjectImp::implementsConstruct() const
+{
+ return true;
}
+
+// ECMA 15.4.2
+Object ArrayObjectImp::construct(ExecState *exec, const List &args)
+{
+ Object result(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype()));
+
+ unsigned int len;
+ ListIterator it = args.begin();
+ // a single argument might denote the array size
+ if (args.size() == 1 && it->type() == NumberType)
+ len = it->toUInt32(exec);
+ else {
+ // initialize array
+ len = args.size();
+ for (unsigned int u = 0; it != args.end(); it++, u++)
+ result.put(exec, UString::from(u), *it);
+ }
+
+ // array size
+ result.put(exec, "length", Number(len), DontEnum | DontDelete);
+ static_cast<ArrayInstanceImp*>(result.imp())->putDirect(exec, "length", Number(len), DontEnum | DontDelete);
+
+ return result;
+}
+
+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);
+}
+
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifndef _ARRAY_OBJECT_H_
#define _ARRAY_OBJECT_H_
-#include "object.h"
-#include "function.h"
+#include "internal.h"
+#include "function_object.h"
namespace KJS {
- class ArrayObject : public ConstructorImp {
+ class ArrayInstanceImp : public ObjectImp {
public:
- ArrayObject(const Object &funcProto, const Object &arrayProto);
- Completion execute(const List &);
- Object construct(const List &);
+ ArrayInstanceImp(const Object &proto);
+
+ virtual void put(ExecState *exec, const UString &propertyName, const Value &value, int attr = None);
+ virtual void putDirect(ExecState *exec, const UString &propertyName, const Value &value, int attr = None);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
};
- class ArrayPrototype : public ObjectImp {
+ class ArrayPrototypeImp : public ArrayInstanceImp {
public:
- ArrayPrototype(const Object& proto);
- virtual KJSO get(const UString &p) const;
+ ArrayPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objProto);
+ Value get(ExecState *exec, const UString &p) const;
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
};
- class ArrayProtoFunc : public InternalFunctionImp {
+ class ArrayProtoFuncImp : public InternalFunctionImp {
public:
- ArrayProtoFunc(int i) : id(i) { }
- Completion execute(const List &);
+ 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
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
*/
-#include "kjs.h"
-#include "operations.h"
+#include "value.h"
+#include "object.h"
#include "types.h"
+#include "interpreter.h"
+#include "operations.h"
#include "bool_object.h"
#include "error_object.h"
+#include <assert.h>
+
using namespace KJS;
-BooleanObject::BooleanObject(const KJSO& funcProto, const KJSO &booleanProto)
- : ConstructorImp(funcProto, 1)
+// ------------------------------ BooleanInstanceImp ---------------------------
+
+const ClassInfo BooleanInstanceImp::info = {"Boolean", 0, 0, 0};
+
+BooleanInstanceImp::BooleanInstanceImp(const Object &proto)
+ : ObjectImp(proto)
{
- // Boolean.prototype
- setPrototypeProperty(booleanProto);
}
-// ECMA 15.6.1
-Completion BooleanObject::execute(const List &args)
-{
- Boolean b;
+// ------------------------------ BooleanPrototypeImp --------------------------
- if (args.isEmpty())
- b = Boolean(false);
- else
- b = args[0].toBoolean();
+// ECMA 15.6.4
+
+BooleanPrototypeImp::BooleanPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto)
+ : BooleanInstanceImp(Object(objectProto))
+{
+ Value protect(this);
+ // The constructor will be added later by InterpreterImp::InterpreterImp()
- return Completion(ReturnValue, b);
+ put(exec,"toString", Object(new BooleanProtoFuncImp(exec,funcProto,BooleanProtoFuncImp::ToString,0)), DontEnum);
+ put(exec,"valueOf", Object(new BooleanProtoFuncImp(exec,funcProto,BooleanProtoFuncImp::ValueOf,0)), DontEnum);
+ setInternalValue(Boolean(false));
}
-// ECMA 15.6.2
-Object BooleanObject::construct(const List &args)
-{
- Boolean b;
- if (args.size() > 0)
- b = args.begin()->toBoolean();
- else
- b = Boolean(false);
- return Object::create(BooleanClass, b);
+// ------------------------------ BooleanProtoFuncImp --------------------------
+
+BooleanProtoFuncImp::BooleanProtoFuncImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto, int i, int len)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ put(exec,"length",Number(len),DontDelete|ReadOnly|DontEnum);
}
-// ECMA 15.6.4
-BooleanPrototype::BooleanPrototype(const Object& proto)
- : ObjectImp(BooleanClass, Boolean(false), proto)
+
+bool BooleanProtoFuncImp::implementsCall() const
{
- // The constructor will be added later in BooleanObject's constructor
+ return true;
}
-KJSO BooleanPrototype::get(const UString &p) const
+
+// ECMA 15.6.4.2 + 15.6.4.3
+Value BooleanProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &/*args*/)
{
- if (p == "toString")
- return Function(new BooleanProtoFunc(ToString));
- else if (p == "valueOf")
- return Function(new BooleanProtoFunc(ValueOf));
+ // no generic function. "this" has to be a Boolean object
+ if (!thisObj.inherits(&BooleanInstanceImp::info)) {
+ Object err = Error::create(exec,TypeError);
+ exec->setException(err);
+ return err;
+ }
+
+ // execute "toString()" or "valueOf()", respectively
+
+ Value v = thisObj.internalValue();
+ assert(!v.isNull());
+
+ if (id == ToString)
+ return String(v.toString(exec));
else
- return Imp::get(p);
+ return Boolean(v.toBoolean(exec)); /* TODO: optimize for bool case */
}
-BooleanProtoFunc::BooleanProtoFunc(int i)
- : id(i)
+// ------------------------------ BooleanObjectImp -----------------------------
+
+
+BooleanObjectImp::BooleanObjectImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ BooleanPrototypeImp *booleanProto)
+ : InternalFunctionImp(funcProto)
{
+ Value protect(this);
+ put(exec,"prototype", Object(booleanProto),DontEnum|DontDelete|ReadOnly);
+
+ // no. of arguments for constructor
+ put(exec,"length", Number(1), ReadOnly|DontDelete|DontEnum);
}
-// ECMA 15.6.4.2 + 15.6.4.3
-Completion BooleanProtoFunc::execute(const List &)
-{
- KJSO result;
- Object thisObj = Object::dynamicCast(thisValue());
+bool BooleanObjectImp::implementsConstruct() const
+{
+ return true;
+}
- // no generic function. "this" has to be a Boolean object
- if (thisObj.isNull() || thisObj.getClass() != BooleanClass) {
- result = Error::create(TypeError);
- return Completion(ReturnValue, result);
- }
+// ECMA 15.6.2
+Object BooleanObjectImp::construct(ExecState *exec, const List &args)
+{
+ Object proto = exec->interpreter()->builtinBooleanPrototype();
+ Object obj(new BooleanInstanceImp(proto));
- // execute "toString()" or "valueOf()", respectively
- KJSO v = thisObj.internalValue();
- if (id == BooleanPrototype::ToString)
- result = v.toString();
+ Boolean b;
+ if (args.size() > 0)
+ b = args.begin()->toBoolean(exec);
else
- result = v.toBoolean();
+ b = Boolean(false);
+
+ obj.setInternalValue(b);
+
+ return obj;
+}
+
+bool BooleanObjectImp::implementsCall() const
+{
+ return true;
+}
- return Completion(ReturnValue, result);
+// 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 */
}
+
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifndef _BOOL_OBJECT_H_
#define _BOOL_OBJECT_H_
-#include "object.h"
-#include "function.h"
+#include "internal.h"
+#include "function_object.h"
namespace KJS {
- class BooleanObject : public ConstructorImp {
+ class BooleanInstanceImp : public ObjectImp {
public:
- BooleanObject(const KJSO& funcProto, const KJSO &booleanProto);
- Completion execute(const List &);
- Object construct(const List &);
+ BooleanInstanceImp(const Object &proto);
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
};
- class BooleanPrototype : public ObjectImp {
+ /**
+ * @internal
+ *
+ * The initial value of Boolean.prototype (and thus all objects created
+ * with the Boolean constructor
+ */
+ class BooleanPrototypeImp : public BooleanInstanceImp {
public:
- BooleanPrototype(const Object& proto);
- virtual KJSO get(const UString &p) const;
- enum { ToString, ValueOf };
+ BooleanPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto);
};
- class BooleanProtoFunc : public InternalFunctionImp {
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Boolean.prototype object
+ */
+ class BooleanProtoFuncImp : public InternalFunctionImp {
public:
- BooleanProtoFunc(int i);
- Completion execute(const List &);
+ BooleanProtoFuncImp(ExecState *exec,
+ FunctionPrototypeImp *funcProto, int i, int len);
+
+ 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
+// -*- 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)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#include "collector.h"
-#include "object.h"
#include "internal.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
+#ifdef KJS_DEBUG_MEM
+#include <typeinfo>
+#endif
namespace KJS {
CollectorBlock* Collector::currentBlock = 0L;
unsigned long Collector::filled = 0;
unsigned long Collector::softLimit = KJS_MEM_INCREMENT;
+
+unsigned long Collector::timesFilled = 0;
+unsigned long Collector::increaseLimitAt = 1;
+
+bool Collector::memLimitReached = false;
+
#ifdef KJS_DEBUG_MEM
bool Collector::collecting = false;
#endif
if (s == 0)
return 0L;
+ // Try and deal with memory requirements in a scalable way. Simple scripts
+ // should only require small amounts of memory, but for complex scripts we don't
+ // want to end up running the garbage collector hundreds of times a second.
if (filled >= softLimit) {
+ timesFilled++;
collect();
- if (filled >= softLimit && softLimit < KJS_MEM_LIMIT) // we are actually using all this memory
+
+ if (filled >= softLimit && softLimit < KJS_MEM_LIMIT) {
+ // Even after collection we are still using more than the limit, so increase
+ // the limit
softLimit *= 2;
+ }
+ else if (timesFilled == increaseLimitAt && increaseLimitAt < 128) {
+ // The allowed memory limit keeps getting reached (lots of objects created
+ // and deleted). Increase it a bit so GC gets run less often.
+ timesFilled = 0;
+ softLimit *= 2;
+ increaseLimitAt *= 2;
+ }
}
void *m = malloc(s);
+#ifdef KJS_DEBUG_MEM
+ //fprintf( stderr, "allocate: size=%d valueimp=%p\n",s,m);
+#endif
- // hack to ensure obj is protected from GC before any constructors are run
- // (prev = marked, next = gcallowed)
- static_cast<Imp*>(m)->prev = 0;
- static_cast<Imp*>(m)->next = 0;
+ // VI_CREATED and VI_GCALLOWED being unset ensure that value
+ // is protected from GC before any constructors are run
+ static_cast<ValueImp*>(m)->_flags = 0;
if (!root) {
root = new CollectorBlock(BlockSize);
if (block->filled >= block->size) {
#ifdef KJS_DEBUG_MEM
- printf("allocating new block of size %d\n", block->size);
+ //fprintf( stderr, "allocating new block of size %d\n", block->size);
#endif
CollectorBlock *tmp = new CollectorBlock(BlockSize);
block->next = tmp;
block->filled++;
if (softLimit >= KJS_MEM_LIMIT) {
- KJScriptImp::setException("Out of memory");
+ memLimitReached = true;
+ fprintf(stderr,"Out of memory");
}
return m;
/**
* Mark-sweep garbage collection.
*/
-void Collector::collect()
+bool Collector::collect()
{
#ifdef KJS_DEBUG_MEM
- printf("collecting %d objects total\n", Imp::count);
- collecting = true;
+ fprintf(stderr,"Collector::collect()\n");
#endif
-
- // MARK: first set all ref counts to 0 ....
+ bool deleted = false;
+ // MARK: first unmark everything
CollectorBlock *block = root;
while (block) {
-#ifdef KJS_DEBUG_MEM
- printf("cleaning block filled %d out of %d\n", block->filled, block->size);
-#endif
- Imp **r = (Imp**)block->mem;
+ ValueImp **r = (ValueImp**)block->mem;
assert(r);
for (int i = 0; i < block->size; i++, r++)
if (*r) {
- (*r)->setMarked(false);
+ (*r)->_flags &= ~ValueImp::VI_MARKED;
}
block = block->next;
}
- // ... increase counter for all referenced objects recursively
+ // mark all referenced objects recursively
// starting out from the set of root objects
- if (KJScriptImp::hook) {
- KJScriptImp *scr = KJScriptImp::hook;
+ 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 != KJScriptImp::hook);
+ } while (scr != InterpreterImp::s_hook);
}
// mark any other objects that we wouldn't delete anyway
block = root;
while (block) {
- Imp **r = (Imp**)block->mem;
+ ValueImp **r = (ValueImp**)block->mem;
assert(r);
for (int i = 0; i < block->size; i++, r++)
- if (*r && (*r)->created() && ((*r)->refcount || !(*r)->gcAllowed()) && !(*r)->marked())
- (*r)->mark();
+ {
+ ValueImp *imp = (*r);
+ // Check for created=true, marked=false and (gcallowed=false or refcount>0)
+ if (imp &&
+ (imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
+ ( (imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount ) ) {
+ //fprintf( stderr, "Collector marking imp=%p\n",(void*)imp);
+ imp->mark();
+ }
+ }
block = block->next;
}
// SWEEP: delete everything with a zero refcount (garbage)
block = root;
while (block) {
- Imp **r = (Imp**)block->mem;
+ ValueImp **r = (ValueImp**)block->mem;
int del = 0;
for (int i = 0; i < block->size; i++, r++) {
- if (*r && ((*r)->refcount == 0) && !(*r)->marked() && (*r)->gcAllowed()) {
- // emulate destructing part of 'operator delete()'
- (*r)->~Imp();
- free(*r);
- *r = 0L;
- del++;
+ ValueImp *imp = (*r);
+ // Can delete if refcount==0, created==true, gcAllowed==true, and marked==false
+ // Make sure to update the test if you add more bits to _flags.
+ if (imp &&
+ !imp->refcount && imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED)) {
+ // emulate destructing part of 'operator delete()'
+ //fprintf( stderr, "Collector::deleting ValueImp %p (%s)\n", (void*)imp, typeid(*imp).name());
+ imp->~ValueImp();
+ free(imp);
+ *r = 0L;
+ del++;
}
}
filled -= del;
block->filled -= del;
block = block->next;
+ if (del)
+ deleted = true;
}
- // delete the emtpy containers
+ // delete the empty containers
block = root;
while (block) {
CollectorBlock *next = block->next;
if (block->filled == 0) {
if (block->prev)
- block->prev->next = next;
+ block->prev->next = next;
if (block == root)
- root = next;
+ root = next;
if (next)
- next->prev = block->prev;
+ next->prev = block->prev;
if (block == currentBlock) // we don't want a dangling pointer
- currentBlock = 0L;
+ currentBlock = 0L;
assert(block != root);
delete block;
}
block = next;
}
+#if 0
+ // This is useful to track down memory leaks
+ static int s_count = 0;
+ fprintf(stderr, "Collector done (was run %d)\n",s_count);
+ if (s_count++ % 50 == 2)
+ finalCheck();
+#endif
+ return deleted;
+}
#ifdef KJS_DEBUG_MEM
- collecting = false;
-#endif
+void Collector::finalCheck()
+{
+ CollectorBlock *block = root;
+ while (block) {
+ ValueImp **r = (ValueImp**)block->mem;
+ for (int i = 0; i < block->size; i++, r++) {
+ if (*r ) {
+ fprintf( stderr, "Collector::finalCheck() still having ValueImp %p (%s) [marked:%d gcAllowed:%d created:%d refcount:%d]\n",
+ (void*)(*r), typeid( **r ).name(),
+ (bool)((*r)->_flags & ValueImp::VI_MARKED),
+ (bool)((*r)->_flags & ValueImp::VI_GCALLOWED),
+ (bool)((*r)->_flags & ValueImp::VI_CREATED),
+ (*r)->refcount);
+ }
+ }
+ block = block->next;
+ }
}
+#endif
+// -*- 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)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifndef _KJSCOLLECTOR_H_
#include <stdlib.h>
+// for DEBUG_*
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
+
namespace KJS {
- class Imp;
class CollectorBlock;
/**
/**
* Run the garbage collection. This involves calling the delete operator
* on each object and freeing the used memory.
- * In the current implemenation this will basically free all registered
- * object regardless whether they are still references by others or not.
- *
*/
- static void collect();
+ static bool collect();
static int size() { return filled; }
+ static bool outOfMemory() { return memLimitReached; }
#ifdef KJS_DEBUG_MEM
+ /** Check that nothing is left when the last interpreter gets deleted */
+ static void finalCheck();
/**
* @internal
*/
static CollectorBlock* currentBlock;
static unsigned long filled;
static unsigned long softLimit;
+ static unsigned long timesFilled;
+ static unsigned long increaseLimitAt;
+ static bool memLimitReached;
enum { BlockSize = 100 };
};
--- /dev/null
+#! /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;
+sub calcTable();
+sub output();
+sub hashValue($);
+
+while (<IN>) {
+ chop;
+ s/^\s*//g;
+ if (/^\#|^$/) {
+ # comment. do nothing
+ } elsif (/^\@begin\s*(\w+)\s*(\d+)\s*$/ && !$inside) {
+ $inside = 1;
+ $name = $1;
+ $hashsize = $2;
+ } 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++;
+ }
+ }
+ $table[$h] = $i;
+ $i++;
+ $maxdepth = $depth if ( $depth > $maxdepth);
+ }
+
+ 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 $i = 0;
+# foreach $entry (@table) {
+# print "$i " . $entry;
+# print " -> " . $links[$i] if (defined($links[$i]));
+# print "\n";
+# $i++;
+# }
+}
+
+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";
+ }
+
+ print "\n#include \"lookup.h\"\n" if ($includelookup);
+ print "\nnamespace KJS {\n";
+ print "\nconst struct HashEntry ${name}Entries[] = {\n";
+ my $i = 0;
+ foreach $entry (@table) {
+ if (defined($entry)) {
+ my $key = $keys[$entry];
+ print " \{ \"" . $key . "\"";
+ print ", " . $values[$entry];
+ print ", " . $attrs[$entry];
+ print ", " . $params[$entry];
+ print ", ";
+ if (defined($links[$i])) {
+ print "&${name}Entries[$links[$i]]" . " \}";
+ } else {
+ print "0 \}"
+ }
+ } else {
+ print " \{ 0, 0, 0, 0, 0 \}";
+ }
+ print "," unless ($i == $size - 1);
+ print "\n";
+ $i++;
+ }
+ print "};\n";
+ print "\nconst struct HashTable $name = ";
+ print "\{ 2, $size, ${name}Entries, $hashsize \};\n\n";
+ print "}; // namespace\n";
+}
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifdef HAVE_CONFIG_H
#include <math.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#include <locale.h>
+#include <ctype.h>
-#include "kjs.h"
#include "date_object.h"
+#include "error_object.h"
+#include "operations.h"
-namespace KJS {
-
- class DateObjectFunc : public InternalFunctionImp {
- public:
- DateObjectFunc(int i) : id(i) { };
- Completion execute(const List &);
- enum { Parse, UTC };
- private:
- int id;
- };
-
- class DateProtoFunc : public InternalFunctionImp {
- public:
- DateProtoFunc(int i, bool u);
- 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:
- int id;
- bool utc;
- };
-
- // helper functions
- KJSO parseDate(const String &s);
- KJSO timeClip(const KJSO &t);
-};
+#include "date_object.lut.h"
using namespace KJS;
-KJSO KJS::parseDate(const String &s)
-{
- UString u = s.value();
- int firstSlash = u.find('/');
- if ( firstSlash == -1 )
- {
- /* TODO parse dates like "December 25, 1995 23:15:00"*/
- fprintf(stderr,"KJS::parseDate parsing for this format isn't implemented\n%s", u.ascii());
- return Number(0);
- }
- else
- {
- // Found 12/31/2099 on some website -> obviously MM/DD/YYYY
- int month = u.substr(0,firstSlash).toULong();
- int secondSlash = u.find('/',firstSlash+1);
- //fprintf(stdout,"KJS::parseDate firstSlash=%d, secondSlash=%d\n", firstSlash, secondSlash);
- if ( secondSlash == -1 )
- {
- fprintf(stderr,"KJS::parseDate parsing for this format isn't implemented\n%s", u.ascii());
- return Number(0);
- }
- int day = u.substr(firstSlash+1,secondSlash-firstSlash-1).toULong();
- int year = u.substr(secondSlash+1).toULong();
- //fprintf(stdout,"KJS::parseDate day=%d, month=%d, year=%d\n", day, month, year);
- struct tm t;
- memset( &t, 0, sizeof(t) );
- year = (year > 2037) ? 2037 : year; // mktime is limited to 2037 !!!
- t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
- t.tm_mon = month-1; // mktime wants 0-11 for some reason
- t.tm_mday = day;
- time_t seconds = mktime(&t);
- if ( seconds == -1 )
- {
- fprintf(stderr,"KJS::parseDate mktime returned -1.\n%s", u.ascii());
- return Undefined();
- }
- else
- return Number(seconds * 1000.0);
- }
-}
+// ------------------------------ DateInstanceImp ------------------------------
-KJSO KJS::timeClip(const KJSO &t)
-{
- /* TODO */
- return t;
-}
+const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
-DateObject::DateObject(const Object& funcProto, const Object &dateProto)
- : ConstructorImp(funcProto, 7)
+DateInstanceImp::DateInstanceImp(const Object &proto)
+ : ObjectImp(proto)
{
- // ECMA 15.9.4.1 Date.prototype
- setPrototypeProperty(dateProto);
}
-// ECMA 15.9.2
-Completion DateObject::execute(const List &)
-{
- time_t t = time(0L);
- UString s(ctime(&t));
-
- // return formatted string minus trailing \n
- return Completion(ReturnValue, String(s.substr(0, s.size() - 1)));
-}
+// ------------------------------ DatePrototypeImp -----------------------------
+
+const ClassInfo DatePrototypeImp::info = {"Date", 0, &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::ToString 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
-// ECMA 15.9.3
-Object DateObject::construct(const List &args)
+DatePrototypeImp::DatePrototypeImp(ExecState *,
+ ObjectPrototypeImp *objectProto)
+ : DateInstanceImp(Object(objectProto))
{
- KJSO value;
-
- int numArgs = args.size();
-
- if (numArgs == 0) { // new Date() ECMA 15.9.3.3
-#if 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 = Number(utc);
- } else if (numArgs == 1) {
- KJSO p = args[0].toPrimitive();
- if (p.isA(StringType))
- value = parseDate(p.toString());
- else
- value = p.toNumber();
- } else {
- struct tm t;
- memset(&t, 0, sizeof(t));
- Number y = args[0].toNumber();
- /* TODO: check for NaN */
- int year = y.toInt32();
- t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
- t.tm_mon = args[1].toInt32();
- t.tm_mday = (numArgs >= 3) ? args[2].toInt32() : 1;
- t.tm_hour = (numArgs >= 4) ? args[3].toInt32() : 0;
- t.tm_min = (numArgs >= 5) ? args[4].toInt32() : 0;
- t.tm_sec = (numArgs >= 6) ? args[5].toInt32() : 0;
- t.tm_isdst = -1;
- int ms = (numArgs >= 7) ? args[6].toInt32() : 0;
- value = Number(mktime(&t) * 1000.0 + ms);
- }
-
- return Object::create(DateClass, timeClip(value));
+ Value protect(this);
+ setInternalValue(Number(NaN));
+ // The constructor will be added later, after DateObjectImp has been built
}
-KJSO DateObject::get(const UString &p) const
+Value DatePrototypeImp::get(ExecState *exec, const UString &propertyName) const
{
- int id;
-
- if (p == "parse")
- id = DateObjectFunc::Parse;
- else if (p == "UTC")
- id = DateObjectFunc::UTC;
- else
- return Imp::get(p);
-
- return Function(new DateObjectFunc(id));
+ return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
}
-// ECMA 15.9.4.2 - 3
-Completion DateObjectFunc::execute(const List &args)
-{
- KJSO result;
-
- if (id == Parse)
- if (args[0].isA(StringType))
- result = parseDate(args[0].toString());
- else
- result = Undefined();
- else {
- struct tm t;
- memset(&t, 0, sizeof(t));
- int n = args.size();
- Number y = args[0].toNumber();
- /* TODO: check for NaN */
- int year = y.toInt32();
- t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
- t.tm_mon = args[1].toInt32();
- t.tm_mday = (n >= 3) ? args[2].toInt32() : 1;
- t.tm_hour = (n >= 4) ? args[3].toInt32() : 0;
- t.tm_min = (n >= 5) ? args[4].toInt32() : 0;
- t.tm_sec = (n >= 6) ? args[5].toInt32() : 0;
- int ms = (n >= 7) ? args[6].toInt32() : 0;
- result = Number(mktime(&t) * 1000.0 + ms);
- }
-
- return Completion(ReturnValue, result);
-}
+// ------------------------------ DateProtoFuncImp -----------------------------
-// ECMA 15.9.4
-DatePrototype::DatePrototype(const Object& proto)
- : ObjectImp(DateClass, Number(NaN), proto)
+DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
+ ), id(abs(i)), utc(i<0)
+ // We use a negative ID to denote the "UTC" variant.
{
- // The constructor will be added later in DateObject's constructor
+ Value protect(this);
+ put(exec,"length",Number(len),DontDelete|ReadOnly|DontEnum);
}
-KJSO DatePrototype::get(const UString &p) const
+bool DateProtoFuncImp::implementsCall() const
{
- int id;
-
- if (p == "toString" || p == "toUTCString")
- id = DateProtoFunc::ToString;
- else if (p == "toDateString")
- id = DateProtoFunc::ToDateString;
- else if (p == "toTimeString")
- id = DateProtoFunc::ToTimeString;
- else if (p == "toLocaleString")
- id = DateProtoFunc::ToLocaleString;
- else if (p == "toLocaleDateString")
- id = DateProtoFunc::ToLocaleDateString;
- else if (p == "toLocaleTimeString")
- id = DateProtoFunc::ToLocaleTimeString;
- else if (p == "valueOf")
- id = DateProtoFunc::ValueOf;
- else if (p == "getTime")
- id = DateProtoFunc::GetTime;
- else if (p == "getFullYear" || p == "getUTCFullYear")
- id = DateProtoFunc::GetFullYear;
- else if (p == "toGMTString")
- id = DateProtoFunc::ToGMTString;
- else if (p == "getMonth" || p == "getUTCMonth")
- id = DateProtoFunc::GetMonth;
- else if (p == "getDate" || p == "getUTCDate")
- id = DateProtoFunc::GetDate;
- else if (p == "getDay" || p == "getUTCDay")
- id = DateProtoFunc::GetDay;
- else if (p == "getHours" || p == "getUTCHours")
- id = DateProtoFunc::GetHours;
- else if (p == "getMinutes" || p == "getUTCMinutes")
- id = DateProtoFunc::GetMinutes;
- else if (p == "getSeconds" || p == "getUTCSeconds")
- id = DateProtoFunc::GetSeconds;
- else if (p == "getMilliseconds" || p == "getUTCMilliseconds")
- id = DateProtoFunc::GetMilliSeconds;
- else if (p == "getTimezoneOffset")
- id = DateProtoFunc::GetTimezoneOffset;
- else if (p == "setTime")
- id = DateProtoFunc::SetTime;
- else if (p == "setMilliseconds" || p == "setUTCMilliseconds")
- id = DateProtoFunc::SetMilliSeconds;
- else if (p == "setSeconds" || p == "setUTCSeconds")
- id = DateProtoFunc::SetSeconds;
- else if (p == "setMinutes" || p == "setUTCMinutes")
- id = DateProtoFunc::SetMinutes;
- else if (p == "setHours" || p == "setUTCHours")
- id = DateProtoFunc::SetHours;
- else if (p == "setDate" || p == "setUTCDate")
- id = DateProtoFunc::SetDate;
- else if (p == "setMonth" || p == "setUTCMonth")
- id = DateProtoFunc::SetMonth;
- else if (p == "setFullYear" || p == "setUTCFullYear")
- id = DateProtoFunc::SetFullYear;
- else if (p == "setYear" )
- id = DateProtoFunc::SetYear;
- // non-normative
- else if (p == "getYear")
- id = DateProtoFunc::GetYear;
- else if (p == "toGMTString")
- id = DateProtoFunc::ToGMTString;
- else
- return Undefined();
-
- bool utc = (p.find("UTC") >= 0) ? true : false;
- return Function(new DateProtoFunc(id, utc));
+ return true;
}
-DateProtoFunc::DateProtoFunc(int i, bool u) : id(i), utc(u)
+Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
{
-}
+ if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
+ !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;
+ }
-Completion DateProtoFunc::execute(const List &args)
-{
- KJSO result;
+
+ Value result;
UString s;
const int bufsize=100;
char timebuffer[bufsize];
char *oldlocale = setlocale(LC_TIME,NULL);
if (!oldlocale)
oldlocale = setlocale(LC_ALL, NULL);
- Object thisObj = Object::dynamicCast(thisValue());
- KJSO v = thisObj.internalValue();
- double milli = v.toNumber().value();
+ Value v = thisObj.internalValue();
+ double milli = v.toNumber(exec);
time_t tv = (time_t) floor(milli / 1000.0);
int ms = int(milli - tv * 1000.0);
case ToTimeString:
case ToGMTString:
setlocale(LC_TIME,"C");
- if (id == DateProtoFunc::ToDateString) {
+ if (id == DateProtoFuncImp::ToDateString) {
strftime(timebuffer, bufsize, "%x",t);
- } else if (id == DateProtoFunc::ToTimeString) {
+ } else if (id == DateProtoFuncImp::ToTimeString) {
strftime(timebuffer, bufsize, "%X",t);
} else {
t = gmtime(&tv);
result = Number(milli);
break;
case GetTime:
- if (thisObj.getClass() == DateClass)
- result = Number(milli);
- else
- result = Error::create(TypeError);
+ result = Number(milli);
break;
case GetYear:
- result = Number(t->tm_year);
+ // IE returns the full year even in getYear.
+ if ( exec->interpreter()->compatMode() == Interpreter::IECompat )
+ result = Number(1900 + t->tm_year);
+ else
+ result = Number(t->tm_year);
break;
case GetFullYear:
result = Number(1900 + t->tm_year);
result = Number(t->tm_sec);
break;
case GetMilliSeconds:
- result = Undefined();
+ result = Number(ms);
break;
case GetTimezoneOffset:
#if defined BSD || defined(__APPLE__)
#endif
break;
case SetTime:
- milli = args[0].round();
+ milli = roundValue(exec,args[0]);
result = Number(milli);
thisObj.setInternalValue(result);
break;
case SetMilliSeconds:
- ms = args[0].toInt32();
+ ms = args[0].toInt32(exec);
break;
case SetSeconds:
- t->tm_sec = args[0].toInt32();
+ t->tm_sec = args[0].toInt32(exec);
break;
case SetMinutes:
- t->tm_min = args[0].toInt32();
+ t->tm_min = args[0].toInt32(exec);
break;
case SetHours:
- t->tm_hour = args[0].toInt32();
+ t->tm_hour = args[0].toInt32(exec);
break;
case SetDate:
- t->tm_mday = args[0].toInt32();
+ t->tm_mday = args[0].toInt32(exec);
break;
case SetMonth:
- t->tm_mon = args[0].toInt32();
+ t->tm_mon = args[0].toInt32(exec);
break;
case SetFullYear:
- t->tm_year = args[0].toInt32() - 1900;
+ t->tm_year = args[0].toInt32(exec) - 1900;
break;
case SetYear:
- t->tm_year = args[0].toInt32() >= 1900 ? args[0].toInt32() - 1900 : args[0].toInt32();
+ t->tm_year = args[0].toInt32(exec) >= 1900 ? args[0].toInt32(exec) - 1900 : args[0].toInt32(exec);
break;
}
thisObj.setInternalValue(result);
}
- return Completion(ReturnValue, 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
+ put(exec,"prototype", Object(dateProto), DontEnum|DontDelete|ReadOnly);
+
+ put(exec,"parse", Object(new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1)), DontEnum);
+ put(exec,"UTC", Object(new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC, 7)), DontEnum);
+
+ // no. of arguments for constructor
+ put(exec,"length", Number(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
+ Value value;
+
+ if (numArgs == 0) { // new Date() ECMA 15.9.3.3
+#if 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 = Number(utc);
+ } else if (numArgs == 1) {
+ UString s = args[0].toString(exec);
+ double d = s.toDouble();
+ if (isNaN(d))
+ value = parseDate(s);
+ else
+ value = Number(d);
+ } else {
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ Number y = args[0].toNumber(exec);
+ // TODO: check for NaN
+ int year = y.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 = Number(mktime(&t) * 1000.0 + ms);
+ }
+
+ Object proto = exec->interpreter()->builtinDatePrototype();
+ Object ret(new DateInstanceImp(proto));
+ ret.setInternalValue(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);
+ UString s(ctime(&t));
+
+ // return formatted string minus trailing \n
+ return String(s.substr(0, s.size() - 1));
+}
+
+// ------------------------------ DateObjectFuncImp ----------------------------
+
+DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ int i, int len)
+ : InternalFunctionImp(funcProto), id(i)
+{
+ Value protect(this);
+ put(exec,"length",Number(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) {
+ if (args[0].type() == StringType)
+ return parseDate(args[0].toString(exec));
+ else
+ return Undefined();
+ }
+ else { // UTC
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ int n = args.size();
+ Number y = args[0].toNumber(exec);
+ // TODO: check for NaN
+ int year = y.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(mktime(&t) * 1000.0 + ms);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+
+Value KJS::parseDate(const String &s)
+{
+ UString u = s.value();
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
+#endif
+ int firstSlash = u.find('/');
+ if ( firstSlash == -1 )
+ {
+ time_t seconds = KRFCDate_parseDate( u );
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"KRFCDate_parseDate returned seconds=%d\n",seconds);
+#endif
+ if ( seconds == -1 )
+ return Undefined();
+ else
+ return Number(seconds * 1000.0);
+ }
+ else
+ {
+ // Found 12/31/2099 on some website -> obviously MM/DD/YYYY
+ int month = u.substr(0,firstSlash).toULong();
+ int secondSlash = u.find('/',firstSlash+1);
+ //fprintf(stdout,"KJS::parseDate firstSlash=%d, secondSlash=%d\n", firstSlash, secondSlash);
+ if ( secondSlash == -1 )
+ {
+ fprintf(stderr,"KJS::parseDate parsing for this format isn't implemented\n%s", u.ascii());
+ return Number(0);
+ }
+ int day = u.substr(firstSlash+1,secondSlash-firstSlash-1).toULong();
+ int year = u.substr(secondSlash+1).toULong();
+ //fprintf(stdout,"KJS::parseDate day=%d, month=%d, year=%d\n", day, month, year);
+ struct tm t;
+ memset( &t, 0, sizeof(t) );
+ year = (year > 2037) ? 2037 : year; // mktime is limited to 2037 !!!
+ t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
+ t.tm_mon = month-1; // mktime wants 0-11 for some reason
+ t.tm_mday = day;
+ time_t seconds = mktime(&t);
+ if ( seconds == -1 )
+ {
+ fprintf(stderr,"KJS::parseDate mktime returned -1.\n%s", u.ascii());
+ return Undefined();
+ }
+ else
+ return Number(seconds * 1000.0);
+ }
+}
+
+///// Awful duplication from krfcdate.cpp - we don't link to kdecore
+
+static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
+{
+ unsigned int 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;
+}
+
+static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
+
+// we follow the recommendation of rfc2822 to consider all
+// obsolete time zones not listed here equivalent to "-0000"
+static const struct {
+ const char *tzName;
+ int tzOffset;
+} known_zones[] = {
+ { "UT", 0 },
+ { "GMT", 0 },
+ { "EST", -300 },
+ { "EDT", -240 },
+ { "CST", -360 },
+ { "CDT", -300 },
+ { "MST", -420 },
+ { "MDT", -360 },
+ { "PST", -480 },
+ { "PDT", -420 },
+ { 0, 0 }
+};
+
+time_t 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 format, added for Javascript:
+ // [Wednesday] January 09 1999 23:12:40 GMT
+ //
+ // We ignore the weekday
+ //
+ time_t result = 0;
+ int offset = 0;
+ char *newPosStr;
+ const char *dateString = _date.ascii();
+ int day = 0;
+ char monthStr[4];
+ int month = -1; // not set yet
+ int year = 0;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+
+ // Skip leading space
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ const char *wordStart = dateString;
+ // Check contents of first words if not number
+ while(*dateString && !isdigit(*dateString))
+ {
+ if ( isspace(*dateString) && dateString - wordStart >= 3 )
+ {
+ monthStr[0] = tolower(*wordStart++);
+ monthStr[1] = tolower(*wordStart++);
+ monthStr[2] = tolower(*wordStart++);
+ monthStr[3] = '\0';
+ //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
+ const char *str = strstr(haystack, monthStr);
+ if (str)
+ month = (str-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
+ while(*dateString && isspace(*dateString))
+ dateString++;
+ wordStart = dateString;
+ }
+ else
+ dateString++;
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ // ' 09-Nov-99 23:12:40 GMT'
+ day = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((day < 1) || (day > 31))
+ return result; // Invalid date;
+ if (!*dateString)
+ return result; // Invalid date
+
+ if (*dateString == '-')
+ dateString++;
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if ( month == -1 ) // not found yet
+ {
+ for(int i=0; i < 3;i++)
+ {
+ if (!*dateString || (*dateString == '-') || isspace(*dateString))
+ return result; // Invalid date
+ monthStr[i] = tolower(*dateString++);
+ }
+ monthStr[3] = '\0';
+
+ newPosStr = (char*)strstr(haystack, monthStr);
+
+ if (!newPosStr)
+ return result; // Invalid date
+
+ month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
+
+ if ((month < 0) || (month > 11))
+ return result; // Invalid date
+
+ while(*dateString && (*dateString != '-') && !isspace(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ // '-99 23:12:40 GMT'
+ if ((*dateString != '-') && !isspace(*dateString))
+ return result; // Invalid date
+ dateString++;
+ }
+
+ if ((month < 0) || (month > 11))
+ return result; // Invalid date
+
+ // '99 23:12:40 GMT'
+ year = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ // Y2K: Solve 2 digit years
+ if ((year >= 0) && (year < 50))
+ year += 2000;
+
+ if ((year >= 50) && (year < 100))
+ year += 1900; // Y2K
+
+ if ((year < 1900) || (year > 2500))
+ return result; // Invalid date
+
+ // Don't fail if the time is missing.
+ if (*dateString)
+ {
+ // ' 23:12:40 GMT'
+ if (!isspace(*dateString++))
+ return result; // Invalid date
+
+ hour = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((hour < 0) || (hour > 23))
+ return result; // Invalid date
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ // ':12:40 GMT'
+ if (*dateString++ != ':')
+ return result; // Invalid date
+
+ minute = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((minute < 0) || (minute > 59))
+ return result; // Invalid date
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ // ':40 GMT'
+ if (*dateString != ':' && !isspace(*dateString))
+ return result; // Invalid date
+
+ // seconds are optional in rfc822 + rfc2822
+ if (*dateString ==':') {
+ dateString++;
+
+ second = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((second < 0) || (second > 59))
+ return result; // Invalid date
+ } else {
+ dateString++;
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+ }
+
+ // don't fail if the time zone is missing, some
+ // broken mail-/news-clients omit the time zone
+ if (*dateString) {
+
+ if ((*dateString == '+') || (*dateString == '-')) {
+ offset = strtol(dateString, &newPosStr, 10);
+
+ if ((offset < -9959) || (offset > 9959))
+ return result; // Invalid date
+
+ int sgn = (offset < 0)? -1:1;
+ offset = abs(offset);
+ offset = ((offset / 100)*60 + (offset % 100))*sgn;
+ } else {
+ for (int i=0; known_zones[i].tzName != 0; i++) {
+ if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
+ offset = known_zones[i].tzOffset;
+ break;
+ }
+ }
+ }
+ }
+ if (sizeof(time_t) == 4)
+ {
+ if ((time_t)-1 < 0)
+ {
+ if (year >= 2038)
+ {
+ year = 2038;
+ month = 0;
+ day = 1;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ }
+ }
+ else
+ {
+ if (year >= 2115)
+ {
+ year = 2115;
+ month = 0;
+ day = 1;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ }
+ }
+ }
+
+ result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
+
+ // avoid negative time values
+ if ((offset > 0) && (offset > result))
+ offset = 0;
+
+ result -= offset*60;
+
+ // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
+ // This is so that parse error and valid epoch 0 return values won't
+ // be the same for sensitive applications...
+ if (result < 1) result = 1;
+
+ return result;
+}
+
+
+Value KJS::timeClip(const Value &t)
+{
+ /* TODO */
+ return t;
+}
+
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifndef _DATE_OBJECT_H_
#define _DATE_OBJECT_H_
-#include "object.h"
-#include "function.h"
+#include "internal.h"
+#include "function_object.h"
namespace KJS {
- class DateObject : public ConstructorImp {
+ class DateInstanceImp : public ObjectImp {
+ public:
+ DateInstanceImp(const Object &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 UString &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:
+ int id;
+ bool utc;
+ };
+
+ /**
+ * @internal
+ *
+ * The initial value of the the global variable's "Date" property
+ */
+ class DateObjectImp : public InternalFunctionImp {
public:
- DateObject(const Object& funcProto, const Object &dateProto);
+ 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 &);
- KJSO get(const UString &p) const;
};
- class DatePrototype : public ObjectImp {
+ /**
+ * @internal
+ *
+ * Class to implement all methods that are properties of the
+ * Date object
+ */
+ class DateObjectFuncImp : public InternalFunctionImp {
public:
- DatePrototype(const Object& proto);
- virtual KJSO get(const UString &p) const;
+ 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
+ Value parseDate(const String &s);
+ time_t KRFCDate_parseDate(const UString &_date);
+ Value timeClip(const Value &t);
+
}; // namespace
#endif
+// -*- 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
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
-#ifdef KJS_DEBUGGER
-
#include "debugger.h"
-#include "kjs.h"
+#include "value.h"
+#include "object.h"
+#include "types.h"
+#include "interpreter.h"
#include "internal.h"
#include "ustring.h"
using namespace KJS;
-Debugger::Debugger(KJScript *engine)
- : eng(0L),
- sid(-1)
-{
- attach(engine);
-}
-
-Debugger::~Debugger()
-{
- detach();
-}
-
-void Debugger::attach(KJScript *e)
-{
- dmode = Disabled;
- if (e) {
- if (!eng || e->rep != eng->rep) {
- eng = e;
- eng->rep->attachDebugger(this);
- }
- } else {
- eng = 0L;
- }
- reset();
-}
+// ------------------------------ Debugger -------------------------------------
-KJScript *Debugger::engine() const
-{
- return eng;
-}
+namespace KJS {
+ struct AttachedInterpreter
+ {
+ public:
+ AttachedInterpreter(Interpreter *i) : interp(i) {}
+ Interpreter *interp;
+ AttachedInterpreter *next;
+ };
-void Debugger::detach()
-{
- reset();
- if (!eng)
- return;
- eng->rep->attachDebugger(0L);
- eng = 0L;
}
-void Debugger::setMode(Mode m)
+Debugger::Debugger()
{
- dmode = m;
+ rep = new DebuggerImp();
}
-Debugger::Mode Debugger::mode() const
+Debugger::~Debugger()
{
- return dmode;
-}
+ // detach from all interpreters
+ while (rep->interps)
+ detach(rep->interps->interp);
-// supposed to be overriden by the user
-bool Debugger::stopEvent()
-{
- return true;
+ delete rep;
}
-void Debugger::callEvent(const UString &, const UString &)
+void Debugger::attach(Interpreter *interp)
{
-}
+ if (interp->imp()->debugger() != this)
+ interp->imp()->setDebugger(this);
-void Debugger::returnEvent()
-{
+ // add to the list of attached interpreters
+ if (!rep->interps)
+ rep->interps = new AttachedInterpreter(interp);
+ else {
+ AttachedInterpreter *ai = rep->interps;
+ while (ai->next)
+ ai = ai->next;
+ ai->next = new AttachedInterpreter(interp);;
+ }
}
-void Debugger::reset()
+void Debugger::detach(Interpreter *interp)
{
- l = -1;
-}
+ if (interp->imp()->debugger() == this)
+ interp->imp()->setDebugger(this);
-int Debugger::freeSourceId() const
-{
- return eng ? eng->rep->sourceId()+1 : -1;
-}
+ // remove from the list of attached interpreters
+ if (rep->interps->interp == interp) {
+ AttachedInterpreter *old = rep->interps;
+ rep->interps = rep->interps->next;
+ delete old;
+ }
-bool Debugger::setBreakpoint(int id, int line)
-{
- if (!eng)
- return false;
- return eng->rep->setBreakpoint(id, line, true);
+ AttachedInterpreter *ai = rep->interps;
+ 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::deleteBreakpoint(int id, int line)
+bool Debugger::sourceParsed(ExecState */*exec*/, int /*sourceId*/,
+ const UString &/*source*/, int /*errorLine*/)
{
- if (!eng)
- return false;
- return eng->rep->setBreakpoint(id, line, false);
+ return true;
}
-void Debugger::clearAllBreakpoints(int id)
+bool Debugger::sourceUnused(ExecState */*exec*/, int /*sourceId*/)
{
- if (!eng)
- return;
- eng->rep->setBreakpoint(id, -1, false);
+ return true;
}
-UString Debugger::varInfo(const UString &ident)
+bool Debugger::exception(ExecState */*exec*/, int /*sourceId*/, int /*lineno*/,
+ Object &/*exceptionObj*/)
{
- if (!eng)
- return UString();
-
- int dot = ident.find('.');
- if (dot < 0)
- dot = ident.size();
- UString sub = ident.substr(0, dot);
- KJSO obj;
- // resolve base
- if (sub == "this") {
- obj = Context::current()->thisValue();
- } else {
- const List *chain = Context::current()->pScopeChain();
- ListIterator scope = chain->begin();
- while (scope != chain->end()) {
- if (scope->hasProperty(ident)) {
- obj = scope->get(ident);
- break;
- }
- scope++;
- }
- if (scope == chain->end())
- return UString();
- }
- // look up each part of a.b.c.
- while (dot < ident.size()) {
- int olddot = dot;
- dot = ident.find('.', olddot+1);
- if (dot < 0)
- dot = ident.size();
- sub = ident.substr(olddot+1, dot-olddot-1);
- obj = obj.get(sub);
- if (!obj.isDefined())
- break;
- }
-
- return sub + "=" + objInfo(obj) + ":" + UString(obj.imp()->typeInfo()->name);
+ return true;
}
-// called by varInfo() and recursively by itself on each properties
-UString Debugger::objInfo(const KJSO &obj) const
+bool Debugger::atStatement(ExecState */*exec*/, int /*sourceId*/, int /*firstLine*/,
+ int /*lastLine*/)
{
- const char *cnames[] = { "Undefined", "Array", "String", "Boolean",
- "Number", "Object", "Date", "RegExp",
- "Error", "Function" };
- PropList *plist = obj.imp()->propList(0, 0, false);
- if (!plist)
- return obj.toString().value();
- else {
- UString result = "{";
- while (1) {
- result += plist->name + "=";
- KJSO p = obj.get(plist->name);
- result += objInfo(p) + ":";
- Object obj = Object::dynamicCast(p);
- if (obj.isNull())
- result += p.imp()->typeInfo()->name;
- else
- result += cnames[int(obj.getClass())];
- plist = plist->next;
- if (!plist)
- break;
- result += ",";
- }
- result += "}";
- return result;
- }
+ return true;
}
-bool Debugger::setVar(const UString &ident, const KJSO &value)
+bool Debugger::callEvent(ExecState */*exec*/, int /*sourceId*/, int /*lineno*/,
+ Object &/*function*/, const List &/*args*/)
{
- if (!eng)
- return false;
- const List *chain = Context::current()->pScopeChain();
- ListIterator scope = chain->begin();
- while (scope != chain->end()) {
- if (scope->hasProperty(ident)) {
- if (!scope->canPut(ident))
- return false;
- scope->put(ident, value);
- return true;
- }
- scope++;
- }
- // didn't find variable
- return false;
+ return true;
}
-// called from the scripting engine each time a statement node is hit.
-bool Debugger::hit(int line, bool breakPoint)
+bool Debugger::returnEvent(ExecState */*exec*/, int /*sourceId*/, int /*lineno*/,
+ Object &/*function*/)
{
- l = line;
- if (!eng)
- return true;
-
- if (!breakPoint && ( mode() == Continue || mode() == Disabled ) )
- return true;
-
- bool ret = stopEvent();
- eng->init(); // in case somebody used a different interpreter meanwhile
- return ret;
+ return true;
}
-#endif
+// -*- 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
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifndef _KJSDEBUGGER_H_
#define _KJSDEBUGGER_H_
-#include "internal.h"
-
namespace KJS {
- //
- // NOTE: this interface is not ready, yet. Do not use unless you
- // don't mind source and binary incompatible changes that may arise
- // before the final version is released.
- //
+ class DebuggerImp;
+ class Interpreter;
+ class ExecState;
+ class Object;
+ class UString;
+ class List;
-#ifdef KJS_DEBUGGER
+ /**
+ * @internal
+ *
+ * Provides an interface which receives notification about various
+ * script-execution related events such as statement execution and function
+ * calls.
+ *
+ * WARNING: This interface is still a work in progress and is not yet
+ * offically publicly available. It is likely to change in binary incompatible
+ * (and possibly source incompatible) ways in future versions. It is
+ * anticipated that at some stage the interface will be frozen and made
+ * available for general use.
+ */
class Debugger {
- friend class KJScriptImp;
- friend class StatementNode;
- friend class DeclaredFunctionImp;
- friend class FunctionImp;
public:
+
/**
- * Available modes of the debugger.
- */
- enum Mode { Disabled = 0, Next, Step, Continue, Stop };
- /**
- * Construct a debugger and attach it to the scripting engine s.
+ * Creates a new debugger
*/
- Debugger(KJScript *s);
+ Debugger();
+
/**
- * Destruct the debugger and detach from the scripting engine we
- * might have been attached to.
+ * 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 scripting engine.
- */
- void attach(KJScript *e);
- /**
- * Returns the engine the interpreter is currently attached to. Null
- * if there isn't any.
- */
- KJScript* engine() const;
- /**
- * Detach the debugger from any scripting engine.
- */
- void detach();
- /**
- * Set debugger into specified mode. This will influence further behaviour
- * if execution of the programm is started or continued.
- */
- virtual void setMode(Mode m);
- /**
- * Returns the current operation mode.
- */
- Mode mode() const;
- /**
- * Returns the line number the debugger currently has stopped at.
- * -1 if the debugger is not in a break status.
- */
- int lineNumber() const { return l; }
- /**
- * Returns the source id the debugger currently has stopped at.
- * -1 if the debugger is not in a break status.
- */
- int sourceId() const { return sid; }
- /**
- * Sets a breakpoint in the first statement where line lies in between
- * the statements range. Returns true if sucessfull, false if no
- * matching statement could be found.
+ * 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()
*/
- bool setBreakpoint(int id, int line);
- bool deleteBreakpoint(int id, int line);
- void clearAllBreakpoints(int id=-1);
+ void attach(Interpreter *interp);
+
/**
- * Returns the value of ident out of the current context in string form
+ * 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()
*/
- UString varInfo(const UString &ident);
+ void detach(Interpreter *interp);
+
/**
- * Set variable ident to value. Returns true if successful, false if
- * the specified variable doesn't exist or isn't writable.
- */
- bool setVar(const UString &ident, const KJSO &value);
+ * 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 @ref 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 succesfully
+ * @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);
- protected:
/**
- * Invoked in case a breakpoint or the next statement is reached in step
- * mode. The return value decides whether execution will continue. True
- * denotes continuation, false an abortion, respectively.
+ * 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. Overload this method if
+ * 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 stopEvent();
+ virtual bool sourceUnused(ExecState *exec, int sourceId);
+
/**
- * Returns an integer that will be assigned to the code passed
- * next to one of the KJScript::evaluate() methods. It's basically
- * a counter to will only be reset to 0 on KJScript::clear().
- *
- * This information is useful in case you evaluate multiple blocks of
- * code containing some function declarations. Keep a map of source id/
- * code pairs, query sourceId() in case of a stopEvent() and update
- * your debugger window with the matching source code.
- */
- int freeSourceId() const;
+ * Called when an exception is thrown during script execution.
+ *
+ * The default implementation does nothing. Override this method if
+ * you want to process this event.
+ *
+ * @param exec The current execution state
+ * @param sourceId The ID of the source code being executed
+ * @param lineno The line at which the error occurred
+ * @param exceptionObj The exception object
+ * @return true if execution should be continue, false if it should
+ * be aborted
+ */
+ virtual bool exception(ExecState *exec, int sourceId, int lineno,
+ Object &exceptionObj);
+
/**
- * Invoked on each function call. Use together with @ref returnEvent
+ * Called when a line of the script is reached (before it is executed)
+ *
+ * The default implementation does nothing. Override this method if
+ * you want to process this event.
+ *
+ * @param exec The current execution state
+ * @param sourceId The ID of the source code being executed
+ * @param firstLine The starting line of the statement that is about to be
+ * executed
+ * @param firstLine The ending line of the statement that is about to be
+ * executed (usually the same as firstLine)
+ * @return true if execution should be continue, false if it should
+ * be aborted
+ */
+ virtual bool atStatement(ExecState *exec, int sourceId, int firstLine,
+ int lastLine);
+ /**
+ * Called on each function call. Use together with @ref #returnEvent
* if you want to keep track of the call stack.
- */
- virtual void callEvent(const UString &fn = UString::null,
- const UString &s = UString::null);
+ *
+ * Note: This only gets called for functions that are declared in ECMAScript
+ * source code or passed to eval(), not for internal KJS or
+ * application-supplied functions.
+ *
+ * The default implementation does nothing. Override this method if
+ * you want to process this event.
+ *
+ * @param exec The current execution state
+ * @param sourceId The ID of the source code being executed
+ * @param lineno The line that is about to be executed
+ * @param function The function being called
+ * @param args The arguments that were passed to the function
+ * line is being executed
+ * @return true if execution should be continue, false if it should
+ * be aborted
+ */
+ virtual bool callEvent(ExecState *exec, int sourceId, int lineno,
+ Object &function, const List &args);
+
/**
- * Invoked on each function exit.
- */
- virtual void returnEvent();
+ * Called on each function exit. The function being returned from is that
+ * which was supplied in the last callEvent().
+ *
+ * Note: This only gets called for functions that are declared in ECMAScript
+ * source code or passed to eval(), not for internal KJS or
+ * application-supplied functions.
+ *
+ * The default implementation does nothing. Override this method if
+ * you want to process this event.
+ *
+ * @param exec The current execution state
+ * @param sourceId The ID of the source code being executed
+ * @param lineno The line that is about to be executed
+ * @param function The function being called
+ * @return true if execution should be continue, false if it should
+ * be aborted
+ */
+ virtual bool returnEvent(ExecState *exec, int sourceId, int lineno,
+ Object &function);
private:
- void reset();
- bool hit(int line, bool breakPoint);
- void setSourceId(int i) { sid = i; }
- UString objInfo(const KJSO &obj) const;
-
- KJScript *eng;
- Mode dmode;
- int l;
- int sid;
+ DebuggerImp *rep;
};
-#endif
};
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
*/
-#include "kjs.h"
-#include "operations.h"
+#include "value.h"
+#include "object.h"
#include "types.h"
-#include "internal.h"
+#include "interpreter.h"
+#include "operations.h"
#include "error_object.h"
-#include "debugger.h"
+//#include "debugger.h"
using namespace KJS;
-const char *ErrorObject::errName[] = {
- "No Error",
- "Error",
- "EvalError",
- "RangeError",
- "ReferenceError",
- "SyntaxError",
- "TypeError",
- "URIError"
-};
-
-ErrorObject::ErrorObject(const Object &funcProto, const Object &errProto,
- ErrorType t)
- : ConstructorImp(funcProto, 1), errType(t)
+// ------------------------------ ErrorPrototypeImp ----------------------------
+
+// ECMA 15.9.4
+ErrorPrototypeImp::ErrorPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto)
+ : ObjectImp(Object(objectProto))
{
- // ECMA 15.11.3.1 Error.prototype
- setPrototypeProperty(errProto);
- const char *n = errName[errType];
+ Value protect(this);
+ setInternalValue(Undefined());
+ // The constructor will be added later in ErrorObjectImp's constructor
- put("name", String(n));
+ put(exec, "name", String("Error"), DontEnum);
+ put(exec, "message", String("Unknown error"), DontEnum);
+ put(exec, "toString", Object(new ErrorProtoFuncImp(exec,funcProto)), DontEnum);
}
-ErrorObject::ErrorObject(const Object& proto, ErrorType t,
- const char *m, int l)
- : ConstructorImp(proto, 1), errType(t)
+// ------------------------------ ErrorProtoFuncImp ----------------------------
+
+ErrorProtoFuncImp::ErrorProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto)
+ : InternalFunctionImp(funcProto)
{
- const char *n = errName[errType];
+ Value protect(this);
+ put(exec,"length",Number(0),DontDelete|ReadOnly|DontEnum);
+}
- put("name", String(n));
- put("message", String(m));
- put("line", Number(l));
-#ifdef KJS_DEBUGGER
- Debugger *dbg = KJScriptImp::current()->debugger();
- if (dbg)
- put("sid", Number(dbg->sourceId()));
-#endif
+bool ErrorProtoFuncImp::implementsCall() const
+{
+ return true;
}
-// ECMA 15.9.2
-Completion ErrorObject::execute(const List &args)
+Value ErrorProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &/*args*/)
{
- // "Error()" gives the sames result as "new Error()"
- return Completion(ReturnValue, construct(args));
+ // toString()
+ UString s = "Error";
+
+ Value v = thisObj.get(exec,"name");
+ if (v.type() != UndefinedType) {
+ s = v.toString(exec);
+ }
+
+ v = thisObj.get(exec,"message");
+ if (v.type() != UndefinedType) {
+ s += ": "+v.toString(exec);
+ }
+
+ return String(s);
+}
+
+// ------------------------------ ErrorObjectImp -------------------------------
+
+ErrorObjectImp::ErrorObjectImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ ErrorPrototypeImp *errorProto)
+ : InternalFunctionImp(funcProto)
+{
+ Value protect(this);
+ // ECMA 15.11.3.1 Error.prototype
+ put(exec, "prototype", Object(errorProto), DontEnum|DontDelete|ReadOnly);
+ //put(exec, "name", String(n));
+}
+
+bool ErrorObjectImp::implementsConstruct() const
+{
+ return true;
}
// ECMA 15.9.3
-Object ErrorObject::construct(const List &args)
+Object ErrorObjectImp::construct(ExecState *exec, const List &args)
{
- if (args.isEmpty() == 1 || !args[0].isDefined())
- return Object::create(ErrorClass, Undefined());
+ Object proto = Object::dynamicCast(exec->interpreter()->builtinErrorPrototype());
+ Object obj(new ObjectImp(proto));
+
+ if (!args.isEmpty() && args[0].type() != UndefinedType) {
+ obj.put(exec,"message", String(args[0].toString(exec)));
+ }
- String message = args[0].toString();
- return Object::create(ErrorClass, message);
+ return obj;
}
-Object ErrorObject::create(ErrorType e, const char *m, int l)
+bool ErrorObjectImp::implementsCall() const
{
- Global global(Global::current());
- KJSO prot = Global::current().get("[[Error.prototype]]");
- assert(prot.isObject());
- Imp *d = new ErrorObject(Object(prot.imp()), e, m, l);
+ return true;
+}
- return Object(d);
+// 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);
}
-// ECMA 15.9.4
-ErrorPrototype::ErrorPrototype(const Object& proto)
- : ObjectImp(ErrorClass, Undefined(), proto)
+// ------------------------------ NativeErrorPrototypeImp ----------------------
+
+NativeErrorPrototypeImp::NativeErrorPrototypeImp(ExecState *exec, ErrorPrototypeImp *errorProto,
+ ErrorType et, UString name, UString message)
+ : ObjectImp(Object(errorProto))
+{
+ Value protect(this);
+ errType = et;
+ put(exec,"name",String(name));
+ put(exec,"message",String(message));
+}
+
+// ------------------------------ NativeErrorImp -------------------------------
+
+const ClassInfo NativeErrorImp::info = {"Error", &InternalFunctionImp::info, 0, 0};
+
+NativeErrorImp::NativeErrorImp(ExecState *exec, FunctionPrototypeImp *funcProto,
+ const Object &prot)
+ : InternalFunctionImp(funcProto), proto(0)
{
- // The constructor will be added later in ErrorObject's constructor
+ Value protect(this);
+ proto = static_cast<ObjectImp*>(prot.imp());
+
+ put(exec,"length",Number(1),DontDelete|ReadOnly|DontEnum); // ECMA 15.11.7.5
+ put(exec,"prototype",prot);
}
-KJSO ErrorPrototype::get(const UString &p) const
+bool NativeErrorImp::implementsConstruct() const
{
- const char *s;
+ return true;
+}
- /* TODO: are these properties dynamic, i.e. should we put() them ? */
- if (p == "name")
- s = "Error";
- else if (p == "message")
- s = "Error message.";
- else if (p == "toString")
- return Function(new ErrorProtoFunc());
- else
- return Imp::get(p);
+Object NativeErrorImp::construct(ExecState *exec, const List &args)
+{
+ Object obj(new ObjectImp(Object(proto)));
+ if (args[0].type() != UndefinedType)
+ obj.put(exec, "message", String(args[0].toString(exec)));
+ return obj;
+}
- return String(s);
+bool NativeErrorImp::implementsCall() const
+{
+ return true;
}
-Completion ErrorProtoFunc::execute(const List &)
+Value NativeErrorImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
{
- // toString()
- const char *s = "Error message.";
+ return construct(exec,args);
+}
- return Completion(ReturnValue, String(s));
+void NativeErrorImp::mark()
+{
+ ObjectImp::mark();
+ if (proto && !proto->marked())
+ proto->mark();
}
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
*/
#ifndef _ERROR_OBJECT_H_
#define _ERROR_OBJECT_H_
-#include "object.h"
-#include "function.h"
+#include "internal.h"
+#include "function_object.h"
namespace KJS {
- class ErrorObject : public ConstructorImp {
+ class ErrorPrototypeImp : public ObjectImp {
public:
- ErrorObject(const Object &funcProto, const Object &errProto,
- ErrorType t = GeneralError);
- ErrorObject(const Object& proto, ErrorType t, const char *m, int l = -1);
- Completion execute(const List &);
- Object construct(const List &);
- static Object create(ErrorType e, const char *m, int ln);
- private:
- ErrorType errType;
- static const char *errName[];
+ ErrorPrototypeImp(ExecState *exec,
+ ObjectPrototypeImp *objectProto,
+ FunctionPrototypeImp *funcProto);
};
- class ErrorPrototype : public ObjectImp {
+ class ErrorProtoFuncImp : public InternalFunctionImp {
public:
- ErrorPrototype(const Object& proto);
- virtual KJSO get(const UString &p) const;
+ ErrorProtoFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto);
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
};
- class ErrorProtoFunc : public InternalFunctionImp {
+ class ErrorObjectImp : public InternalFunctionImp {
public:
- ErrorProtoFunc() { }
- Completion execute(const List &);
+ 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
+// -*- 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)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
+ *
*/
#include "function.h"
-#include "kjs.h"
-#include "types.h"
#include "internal.h"
-#include "operations.h"
+#include "function_object.h"
+#include "lexer.h"
#include "nodes.h"
-#ifndef NDEBUG
+#include "operations.h"
+#include "debugger.h"
+
#include <stdio.h>
-#endif
+#include <assert.h>
+#include <string.h>
using namespace KJS;
-const TypeInfo FunctionImp::info = { "Function", FunctionType,
- &ObjectImp::info, 0, 0 };
-const TypeInfo InternalFunctionImp::info = { "InternalFunction",
- InternalFunctionType,
- &FunctionImp::info, 0, 0 };
-const TypeInfo ConstructorImp::info = { "Function", ConstructorType,
- &InternalFunctionImp::info, 0, 0 };
+// ------------------------------ FunctionImp ----------------------------------
-namespace KJS {
+const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
+namespace KJS {
class Parameter {
public:
Parameter(const UString &n) : name(n), next(0L) { }
UString name;
Parameter *next;
};
-
};
-FunctionImp::FunctionImp()
- : ObjectImp(/*TODO*/BooleanClass), param(0L)
+FunctionImp::FunctionImp(ExecState *exec, const UString &n)
+ : InternalFunctionImp(
+ static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
+ ), param(0L), ident(n), argStack(0)
{
+ Value protect(this);
+ argStack = new ListImp();
+ put(exec,"arguments",Null(),ReadOnly|DontDelete|DontEnum);
}
-FunctionImp::FunctionImp(const UString &n)
- : ObjectImp(/*TODO*/BooleanClass), ident(n), param(0L)
+FunctionImp::~FunctionImp()
{
+ argStack->setGcAllowed();
+ // The function shouldn't be deleted while it is still executed; argStack
+ // should be set to 0 by the last call to popArgs()
+ assert(argStack->isEmpty());
+ delete param;
}
-FunctionImp::~FunctionImp()
+void FunctionImp::mark()
{
- delete param;
+ InternalFunctionImp::mark();
+ if (argStack && !argStack->marked())
+ argStack->mark();
}
-KJSO FunctionImp::thisValue() const
+bool FunctionImp::implementsCall() const
{
- return KJSO(Context::current()->thisValue());
+ return true;
+}
+
+Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ Object globalObj = exec->interpreter()->globalObject();
+
+ Debugger *dbg = exec->interpreter()->imp()->debugger();
+ int sid = -1;
+ int lineno = -1;
+ if (dbg) {
+ if (inherits(&DeclaredFunctionImp::info)) {
+ sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
+ lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
+ }
+
+ Object func(this);
+ int cont = dbg->callEvent(exec,sid,lineno,func,args);
+ if (!cont) {
+ dbg->imp()->abort();
+ return Undefined();
+ }
+ }
+
+ // enter a new execution context
+ ContextImp *ctx = new ContextImp(globalObj, exec, thisObj,
+ codeType(), exec->context().imp(), this, args);
+ ExecState *newExec = new ExecState(exec->interpreter(),ctx);
+ newExec->setException(exec->exception()); // could be null
+
+ // In order to maintain our "arguments" property, we maintain a list of arguments
+ // properties from earlier in the execution stack. Upon return, we restore the
+ // previous arguments object using popArgs().
+ // Note: this does not appear to be part of the spec
+ if (codeType() == FunctionCode) {
+ assert(ctx->activationObject().inherits(&ActivationImp::info));
+ Object argsObj = static_cast<ActivationImp*>(ctx->activationObject().imp())->argumentsObject();
+ put(newExec,"arguments", argsObj, DontDelete|DontEnum|ReadOnly);
+ pushArgs(newExec,argsObj);
+ }
+
+ // assign user supplied arguments to parameters
+ processParameters(newExec,args);
+ // add variable declarations (initialized to undefined)
+ processVarDecls(newExec);
+
+ Completion comp = execute(newExec);
+
+ // if an exception occured, propogate it back to the previous execution object
+ if (newExec->hadException())
+ exec->setException(newExec->exception());
+ if (codeType() == FunctionCode)
+ popArgs(newExec);
+ delete newExec;
+ delete ctx;
+
+#ifdef KJS_VERBOSE
+ if (comp.complType() == Throw)
+ printInfo(exec,"throwing", comp.value());
+ else if (comp.complType() == ReturnValue)
+ printInfo(exec,"returning", comp.value());
+ else
+ fprintf(stderr, "returning: undefined\n");
+#endif
+
+ if (dbg) {
+ Object func(this);
+ int cont = dbg->returnEvent(exec,sid,lineno,func);
+ if (!cont) {
+ dbg->imp()->abort();
+ return Undefined();
+ }
+ }
+
+ 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 UString &n)
*p = new Parameter(n);
}
-void FunctionImp::setLength(int l)
-{
- put("length", Number(l), ReadOnly|DontDelete|DontEnum);
-}
-// ECMA 10.1.3
-void FunctionImp::processParameters(const List *args)
+// ECMA 10.1.3q
+void FunctionImp::processParameters(ExecState *exec, const List &args)
{
- KJSO variable = Context::current()->variableObject();
-
- assert(args);
+ Object variable = exec->context().imp()->variableObject();
#ifdef KJS_VERBOSE
fprintf(stderr, "---------------------------------------------------\n"
#endif
if (param) {
- ListIterator it = args->begin();
+ ListIterator it = args.begin();
Parameter **p = ¶m;
while (*p) {
- if (it != args->end()) {
+ if (it != args.end()) {
#ifdef KJS_VERBOSE
fprintf(stderr, "setting parameter %s ", (*p)->name.ascii());
- printInfo("to", *it);
+ printInfo(exec,"to", *it);
#endif
- variable.put((*p)->name, *it);
+ variable.put(exec,(*p)->name, *it);
it++;
} else
- variable.put((*p)->name, Undefined());
+ variable.put(exec,(*p)->name, Undefined());
p = &(*p)->next;
}
}
#ifdef KJS_VERBOSE
else {
- for (int i = 0; i < args->size(); i++)
- printInfo("setting argument", (*args)[i]);
- }
-#endif
-#ifdef KJS_DEBUGGER
- if (KJScriptImp::current()->debugger()) {
- UString argStr;
- for (int i = 0; i < args->size(); i++) {
- if (i > 0)
- argStr += ", ";
- Imp *a = (*args)[i].imp();
- argStr += a->toString().value() + " : " + UString(a->typeInfo()->name);
- }
- UString n = name().isEmpty() ? UString( "(internal)" ) : name();
- KJScriptImp::current()->debugger()->callEvent(n, argStr);
+ for (int i = 0; i < args.size(); i++)
+ printInfo(exec,"setting argument", args[i]);
}
#endif
}
-// ECMA 13.2.1
-KJSO FunctionImp::executeCall(Imp *thisV, const List *args)
+void FunctionImp::processVarDecls(ExecState */*exec*/)
{
- return executeCall(thisV,args,0);
}
-KJSO FunctionImp::executeCall(Imp *thisV, const List *args, const List *extraScope)
+void FunctionImp::pushArgs(ExecState *exec, const Object &args)
{
- bool dummyList = false;
- if (!args) {
- args = new List();
- dummyList = true;
- }
-
- KJScriptImp *curr = KJScriptImp::current();
- Context *save = curr->context();
-
- Context *ctx = new Context(codeType(), save, this, args, thisV);
- curr->setContext(ctx);
+ argStack->append(args);
+ put(exec,"arguments",args,ReadOnly|DontDelete|DontEnum);
+}
- int numScopes = 0;
- if (extraScope) {
- ListIterator scopeIt = extraScope->begin();
- for (; scopeIt != extraScope->end(); scopeIt++) {
- KJSO obj(*scopeIt);
- ctx->pushScope(obj);
- numScopes++;
- }
+void FunctionImp::popArgs(ExecState *exec)
+{
+ argStack->removeLast();
+ if (argStack->isEmpty()) {
+ put(exec,"arguments",Null(),ReadOnly|DontDelete|DontEnum);
}
-
- // assign user supplied arguments to parameters
- processParameters(args);
-
- Completion comp = execute(*args);
-
- if (dummyList)
- delete args;
-
- int i;
- for (i = 0; i < numScopes; i++)
- ctx->popScope();
-
- put("arguments", Null());
- delete ctx;
- curr->setContext(save);
-
-#ifdef KJS_VERBOSE
- if (comp.complType() == Throw)
- printInfo("throwing", comp.value());
- else if (comp.complType() == ReturnValue)
- printInfo("returning", comp.value());
else
- fprintf(stderr, "returning: undefined\n");
-#endif
-#ifdef KJS_DEBUGGER
- if (KJScriptImp::current()->debugger())
- KJScriptImp::current()->debugger()->returnEvent();
-#endif
-
- if (comp.complType() == Throw)
- return comp.value();
- else if (comp.complType() == ReturnValue)
- return comp.value();
- else
- return Undefined();
+ put(exec,"arguments",argStack->at(argStack->size()-1),ReadOnly|DontDelete|DontEnum);
}
-UString FunctionImp::name() const
-{
- return ident;
-}
+// ------------------------------ DeclaredFunctionImp --------------------------
+
+// ### is "Function" correct here?
+const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
-InternalFunctionImp::InternalFunctionImp()
+DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const UString &n,
+ FunctionBodyNode *b, const List &sc)
+ : FunctionImp(exec,n), body(b)
{
+ Value protect(this);
+ body->ref();
+ setScope(sc.copy());
}
-InternalFunctionImp::InternalFunctionImp(int l)
+DeclaredFunctionImp::~DeclaredFunctionImp()
{
- if (l >= 0)
- setLength(l);
+ if ( body->deref() )
+ delete body;
}
-InternalFunctionImp::InternalFunctionImp(const UString &n)
- : FunctionImp(n)
+bool DeclaredFunctionImp::implementsConstruct() const
{
+ return true;
}
-String InternalFunctionImp::toString() const
+// ECMA 13.2.2 [[Construct]]
+Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
{
- if (name().isNull())
- return UString("(Internal function)");
+ Object proto;
+ Value p = get(exec,"prototype");
+ if (p.type() == ObjectType)
+ proto = Object(static_cast<ObjectImp*>(p.imp()));
else
- return UString("function " + name() + "()");
-}
+ proto = exec->interpreter()->builtinObjectPrototype();
-Completion InternalFunctionImp::execute(const List &)
-{
- return Completion(ReturnValue, Undefined());
-}
+ Object obj(new ObjectImp(proto));
-ConstructorImp::ConstructorImp() {
- setPrototype(Global::current().functionPrototype());
- // TODO ??? put("constructor", this);
- setLength(1);
-}
+ Value res = call(exec,obj,args);
-ConstructorImp::ConstructorImp(const UString &n)
- : InternalFunctionImp(n)
-{
+ if (res.type() == ObjectType)
+ return Object::dynamicCast(res);
+ else
+ return obj;
}
-ConstructorImp::ConstructorImp(const KJSO &p, int len)
+Completion DeclaredFunctionImp::execute(ExecState *exec)
{
- setPrototype(p);
- // TODO ??? put("constructor", *this);
- setLength(len);
+ Completion result = body->execute(exec);
+
+ if (result.complType() == Throw || result.complType() == ReturnValue)
+ return result;
+ return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
}
-ConstructorImp::ConstructorImp(const UString &n, const KJSO &p, int len)
- : InternalFunctionImp(n)
+void DeclaredFunctionImp::processVarDecls(ExecState *exec)
{
- setPrototype(p);
- // TODO ??? put("constructor", *this);
- setLength(len);
+ body->processVarDecls(exec);
}
-ConstructorImp::~ConstructorImp() { }
+// ------------------------------ ArgumentsImp ---------------------------------
-Completion ConstructorImp::execute(const List &)
-{
- /* TODO */
- return Completion(ReturnValue, Null());
-}
+const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
-Function::Function(Imp *d)
- : KJSO(d)
+// ECMA 10.1.8
+ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
+ : ObjectImp(exec->interpreter()->builtinObjectPrototype())
{
- if (d) {
- static_cast<FunctionImp*>(rep)->attr = ImplicitNone;
- assert(Global::current().hasProperty("[[Function.prototype]]"));
- setPrototype(Global::current().functionPrototype());
+ Value protect(this);
+ put(exec,"callee", Object(func), DontEnum);
+ put(exec,"length", Number(args.size()), DontEnum);
+ if (!args.isEmpty()) {
+ ListIterator arg = args.begin();
+ for (int i = 0; arg != args.end(); arg++, i++) {
+ put(exec,UString::from(i), *arg, DontEnum);
+ }
}
}
-Completion Function::execute(const List &args)
-{
- assert(rep);
- return static_cast<FunctionImp*>(rep)->execute(args);
-}
+// ------------------------------ ActivationImp --------------------------------
-bool Function::hasAttribute(FunctionAttribute a) const
-{
- assert(rep);
- return static_cast<FunctionImp*>(rep)->hasAttribute(a);
-}
+const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
-#if 0
-InternalFunction::InternalFunction(Imp *d)
- : Function(d)
+// ECMA 10.1.6
+ActivationImp::ActivationImp(ExecState *exec, FunctionImp *f, const List &args)
+ : ObjectImp()
{
- param = 0L;
+ Value protect(this);
+ arguments = new ArgumentsImp(exec,f, args);
+ put(exec, "arguments", Object(arguments), Internal|DontDelete);
}
-InternalFunction::~InternalFunction()
+ActivationImp::~ActivationImp()
{
+ arguments->setGcAllowed();
}
-#endif
-Constructor::Constructor(Imp *d)
- : Function(d)
-{
- if (d) {
- assert(Global::current().hasProperty("[[Function.prototype]]"));
- setPrototype(Global::current().get("[[Function.prototype]]"));
- put("constructor", *this);
- KJSO tmp(d); // protect from GC
- ((FunctionImp*)d)->setLength(1);
- }
-}
+// ------------------------------ GlobalFunc -----------------------------------
-#if 0
-Constructor::Constructor(const Object& proto, int len)
-{
- setPrototype(proto);
- put("constructor", *this);
- put("length", len, DontEnum);
-}
-#endif
-Constructor::~Constructor()
+GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len)
+ : InternalFunctionImp(funcProto), id(i)
{
+ Value protect(this);
+ put(exec,"length",Number(len),DontDelete|ReadOnly|DontEnum);
}
-Completion Constructor::execute(const List &)
+CodeType GlobalFuncImp::codeType() const
{
- /* TODO: call construct instead ? */
- return Completion(ReturnValue, Undefined());
+ return id == Eval ? EvalCode : codeType();
}
-Object Constructor::construct(const List &args)
+bool GlobalFuncImp::implementsCall() const
{
- assert(rep && rep->type() == ConstructorType);
- return ((ConstructorImp*)rep)->construct(args);
+ return true;
}
-Constructor Constructor::dynamicCast(const KJSO &obj)
+Value GlobalFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
{
- // return null object on type mismatch
- if (!obj.isA(ConstructorType))
- return Constructor(0L);
-
- return Constructor(obj.imp());
-}
+ Value res;
+
+ static const char non_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789@*_+-./";
+
+ if (id == Eval) { // eval()
+ Value x = args[0];
+ if (x.type() != StringType)
+ return x;
+ else {
+ UString s = x.toString(exec);
+
+ int sid;
+ int errLine;
+ UString errMsg;
+ ProgramNode *progNode = Parser::parse(s.data(),s.size(),&sid,&errLine,&errMsg);
+
+ // no program node means a syntax occurred
+ if (!progNode) {
+ Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
+ err.put(exec,"sid",Number(sid));
+ exec->setException(err);
+ return err;
+ }
+
+ progNode->ref();
+
+ // enter a new execution context
+ Object glob(exec->interpreter()->globalObject());
+ Object thisVal(Object::dynamicCast(exec->context().thisValue()));
+ ContextImp *ctx = new ContextImp(glob,
+ exec,
+ thisVal,
+ EvalCode,
+ exec->context().imp());
+
+ ExecState *newExec = new ExecState(exec->interpreter(),ctx);
+ newExec->setException(exec->exception()); // could be null
+
+ // execute the code
+ Completion c = progNode->execute(newExec);
+
+ // if an exception occured, propogate it back to the previous execution object
+ if (newExec->hadException())
+ exec->setException(newExec->exception());
+ delete newExec;
+ delete ctx;
+
+ if ( progNode->deref() )
+ delete progNode;
+ if (c.complType() == ReturnValue)
+ return c;
+ // ### setException() on throw?
+ else if (c.complType() == Normal) {
+ if (c.isValueCompletion())
+ return c.value();
+ else
+ return Undefined();
+ } else
+ return c;
+ }
+ } else if (id == ParseInt) {
+ String str = args[0].toString(exec);
+ int radix = args[1].toInt32(exec);
+ if (radix == 0)
+ radix = 10;
+ else if (radix < 2 || radix > 36) {
+ res = Number(NaN);
+ return res;
+ }
+ /* TODO: use radix */
+ // Can't use toULong(), we want to accept floating point values too
+ double value = str.value().toDouble( true /*tolerant*/ );
+ if ( isNaN(value) )
+ res = Number(NaN);
+ else
+ res = Number(static_cast<long>(value)); // remove floating-point part
+ } else if (id == ParseFloat) {
+ String str = args[0].toString(exec);
+ res = Number(str.value().toDouble( true /*tolerant*/ ));
+ } else if (id == IsNaN) {
+ res = Boolean(isNaN(args[0].toNumber(exec)));
+ } else if (id == IsFinite) {
+ Number n = args[0].toNumber(exec);
+ res = Boolean(!n.isNaN() && !n.isInf());
+ } else if (id == 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->unicode();
+ if (u > 255) {
+ char tmp[7];
+ sprintf(tmp, "%%u%04X", u);
+ s = UString(tmp);
+ } else if (strchr(non_escape, (char)u)) {
+ s = UString(c, 1);
+ } else {
+ char tmp[4];
+ sprintf(tmp, "%%%02X", u);
+ s = UString(tmp);
+ }
+ r += s;
+ }
+ res = String(r);
+ } else if (id == 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')) {
+ u = Lexer::convertUnicode((c+2)->unicode(), (c+3)->unicode(),
+ (c+4)->unicode(), (c+5)->unicode());
+ c = &u;
+ k += 5;
+ } else if (*c == UChar('%') && k <= len - 3) {
+ u = UChar(Lexer::convertHex((c+1)->unicode(), (c+2)->unicode()));
+ c = &u;
+ k += 2;
+ }
+ k++;
+ s += UString(c, 1);
+ }
+ res = String(s);
+ }
-KJSO Function::thisValue() const
-{
- return KJSO(Context::current()->thisValue());
+ return res;
}
+// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
+ *
+ * $Id$
*/
#ifndef _KJS_FUNCTION_H_
#define _KJS_FUNCTION_H_
-#include <assert.h>
-
-#include "object.h"
-#include "types.h"
+#include "internal.h"
namespace KJS {
- enum CodeType { GlobalCode,
- EvalCode,
- FunctionCode,
- AnonymousCode,
- HostCode };
-
- enum FunctionAttribute { ImplicitNone, ImplicitThis, ImplicitParents };
-
- class Function;
class Parameter;
/**
- * @short Implementation class for Functions.
+ * @short Implementation class for internal Functions.
*/
- class FunctionImp : public ObjectImp {
+ class FunctionImp : public InternalFunctionImp {
friend class Function;
+ friend class ActivationImp;
public:
- FunctionImp();
- FunctionImp(const UString &n);
+ FunctionImp(ExecState *exec, const UString &n = UString::null);
virtual ~FunctionImp();
- virtual const TypeInfo* typeInfo() const { return &info; }
- static const TypeInfo info;
- virtual Completion execute(const List &) = 0;
- bool hasAttribute(FunctionAttribute a) const { return (attr & a) != 0; }
- virtual CodeType codeType() const = 0;
- KJSO thisValue() const;
+
+ virtual void mark();
+
+ virtual bool implementsCall() const;
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
void addParameter(const UString &n);
- void setLength(int l);
- KJSO executeCall(Imp *thisV, const List *args);
- KJSO executeCall(Imp *thisV, const List *args, const List *extraScope);
- UString name() const;
+ virtual CodeType codeType() const = 0;
+
+ virtual Completion execute(ExecState *exec) = 0;
+ UString name() const { return ident; }
+
+ virtual const ClassInfo *classInfo() const { return &info; }
+ static const ClassInfo info;
protected:
- UString ident;
- FunctionAttribute attr;
Parameter *param;
+ UString ident;
+
private:
- void processParameters(const List *);
+ void processParameters(ExecState *exec, const List &);
+ virtual void processVarDecls(ExecState *exec);
+
+ void pushArgs(ExecState *exec, const Object &args);
+ void popArgs(ExecState *exec);
+ ListImp *argStack;
};
- /**
- * @short Abstract base class for internal functions.
- */
- class InternalFunctionImp : public FunctionImp {
+ class DeclaredFunctionImp : public FunctionImp {
public:
- InternalFunctionImp();
- InternalFunctionImp(int l);
- InternalFunctionImp(const UString &n);
- virtual ~InternalFunctionImp() { }
- virtual String toString() const;
- virtual KJSO toPrimitive(Type) const { return toString(); }
- virtual const TypeInfo* typeInfo() const { return &info; }
- static const TypeInfo info;
- virtual Completion execute(const List &);
- virtual CodeType codeType() const { return HostCode; }
+ DeclaredFunctionImp(ExecState *exec, const UString &n,
+ FunctionBodyNode *b, const List &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; }
+ static const ClassInfo info;
+ private:
+ virtual void processVarDecls(ExecState *exec);
};
- /**
- * @short Base class for Function objects.
- */
- class Function : public KJSO {
+
+
+
+ class ArgumentsImp : public ObjectImp {
public:
- Function(Imp *);
- virtual ~Function() { }
- Completion execute(const List &);
- bool hasAttribute(FunctionAttribute a) const;
- CodeType codeType() const { return HostCode; }
- KJSO thisValue() const;
+ ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args);
+
+ virtual const