Index: trunk/kdelibs/kjs/lexer.cpp =================================================================== --- trunk/kdelibs/kjs/lexer.cpp (revision 123087) +++ trunk/kdelibs/kjs/lexer.cpp (revision 123088) @@ -1,775 +1,783 @@ // -*- c-basic-offset: 2 -*- /* * This file is part of the KDE libraries * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * $Id$ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "value.h" #include "object.h" #include "types.h" #include "interpreter.h" #include "nodes.h" #include "lexer.h" #include "ustring.h" #include "lookup.h" #include "internal.h" // we can't specify the namespace in yacc's C output, so do it here using namespace KJS; static Lexer *currLexer = 0; #ifndef KDE_USE_FINAL #include "grammar.h" #endif #include "lexer.lut.h" extern YYLTYPE yylloc; // global bison variable holding token info // a bridge for yacc from the C world to C++ int kjsyylex() { return Lexer::curr()->lex(); } Lexer::Lexer() : yylineno(0), size8(128), size16(128), restrKeyword(false), stackToken(-1), pos(0), code(0), length(0), #ifndef KJS_PURE_ECMA bol(true), #endif current(0), next1(0), next2(0), next3(0) { // allocate space for read buffers buffer8 = new char[size8]; buffer16 = new UChar[size16]; currLexer = this; } Lexer::~Lexer() { delete [] buffer8; delete [] buffer16; } Lexer *Lexer::curr() { if (!currLexer) { // create singleton instance currLexer = new Lexer(); } return currLexer; } void Lexer::setCode(const UChar *c, unsigned int len) { yylineno = 0; restrKeyword = false; delimited = false; stackToken = -1; pos = 0; code = c; length = len; skipLF = false; skipCR = false; #ifndef KJS_PURE_ECMA bol = true; #endif // read first characters current = (length > 0) ? code[0].unicode() : 0; next1 = (length > 1) ? code[1].unicode() : 0; next2 = (length > 2) ? code[2].unicode() : 0; next3 = (length > 3) ? code[3].unicode() : 0; } void Lexer::shift(unsigned int p) { while (p--) { pos++; current = next1; next1 = next2; next2 = next3; next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0; } } void Lexer::setDone(State s) { state = s; done = true; } int Lexer::lex() { int token = 0; state = Start; unsigned short stringType = 0; // either single or double quotes pos8 = pos16 = 0; done = false; terminator = false; skipLF = false; skipCR = false; // did we push a token on the stack previously ? // (after an automatic semicolon insertion) if (stackToken >= 0) { setDone(Other); token = stackToken; stackToken = 0; } while (!done) { if (skipLF && current != '\n') // found \r but not \n afterwards skipLF = false; if (skipCR && current != '\r') // found \n but not \r afterwards skipCR = false; if (skipLF || skipCR) // found \r\n or \n\r -> eat the second one { skipLF = false; skipCR = false; shift(1); } switch (state) { case Start: if (isWhiteSpace()) { // do nothing } else if (current == '/' && next1 == '/') { shift(1); state = InSingleLineComment; } else if (current == '/' && next1 == '*') { shift(1); state = InMultiLineComment; } else if (current == 0) { if (!terminator && !delimited) { // automatic semicolon insertion if program incomplete token = ';'; stackToken = 0; setDone(Other); } else setDone(Eof); } else if (isLineTerminator()) { yylineno++; #ifndef KJS_PURE_ECMA bol = true; #endif terminator = true; if (restrKeyword) { token = ';'; setDone(Other); } } else if (current == '"' || current == '\'') { state = InString; stringType = current; } else if (isIdentLetter(current)) { record16(current); state = InIdentifier; } else if (current == '0') { record8(current); state = InNum0; } else if (isDecimalDigit(current)) { record8(current); state = InNum; } else if (current == '.' && isDecimalDigit(next1)) { record8(current); state = InDecimal; #ifndef KJS_PURE_ECMA // } else if (bol && current == '-' && next1 == '-' && next2 == '>') { shift(2); state = InSingleLineComment; #endif } else { token = matchPunctuator(current, next1, next2, next3); if (token != -1) { setDone(Other); } else { // cerr << "encountered unknown character" << endl; setDone(Bad); } } break; case InString: if (current == stringType) { shift(1); setDone(String); } else if (current == 0 || isLineTerminator()) { setDone(Bad); } else if (current == '\\') { state = InEscapeSequence; } else { record16(current); } break; // Escape Sequences inside of strings case InEscapeSequence: if (isOctalDigit(current)) { if (current >= '0' && current <= '3' && isOctalDigit(next1) && isOctalDigit(next2)) { record16(convertOctal(current, next1, next2)); shift(2); state = InString; } else if (isOctalDigit(current) && isOctalDigit(next1)) { record16(convertOctal('0', current, next1)); shift(1); state = InString; } else if (isOctalDigit(current)) { record16(convertOctal('0', '0', current)); state = InString; } else { setDone(Bad); } } else if (current == 'x') state = InHexEscape; else if (current == 'u') state = InUnicodeEscape; else { record16(singleEscape(current)); state = InString; } break; case InHexEscape: if (isHexDigit(current) && isHexDigit(next1)) { state = InString; record16(convertHex(current, next1)); shift(1); } else if (current == stringType) { record16('x'); shift(1); setDone(String); } else { record16('x'); record16(current); state = InString; } break; case InUnicodeEscape: if (isHexDigit(current) && isHexDigit(next1) && isHexDigit(next2) && isHexDigit(next3)) { record16(convertUnicode(current, next1, next2, next3)); shift(3); state = InString; } else if (current == stringType) { record16('u'); shift(1); setDone(String); } else { setDone(Bad); } break; case InSingleLineComment: if (isLineTerminator()) { yylineno++; terminator = true; #ifndef KJS_PURE_ECMA bol = true; #endif if (restrKeyword) { token = ';'; setDone(Other); } else state = Start; } else if (current == 0) { setDone(Eof); } break; case InMultiLineComment: if (current == 0) { setDone(Bad); } else if (isLineTerminator()) { yylineno++; } else if (current == '*' && next1 == '/') { state = Start; shift(1); } break; case InIdentifier: if (isIdentLetter(current) || isDecimalDigit(current)) { record16(current); break; } setDone(Identifier); break; case InNum0: if (current == 'x' || current == 'X') { record8(current); state = InHex; } else if (current == '.') { record8(current); state = InDecimal; } else if (current == 'e' || current == 'E') { record8(current); state = InExponentIndicator; } else if (isOctalDigit(current)) { record8(current); state = InOctal; } else if (isDecimalDigit(current)) { record8(current); state = InDecimal; } else { setDone(Number); } break; case InHex: if (isHexDigit(current)) { record8(current); } else { setDone(Hex); } break; case InOctal: if (isOctalDigit(current)) { record8(current); } else if (isDecimalDigit(current)) { record8(current); state = InDecimal; } else setDone(Octal); break; case InNum: if (isDecimalDigit(current)) { record8(current); } else if (current == '.') { record8(current); state = InDecimal; } else if (current == 'e' || current == 'E') { record8(current); state = InExponentIndicator; } else setDone(Number); break; case InDecimal: if (isDecimalDigit(current)) { record8(current); } else if (current == 'e' || current == 'E') { record8(current); state = InExponentIndicator; } else setDone(Number); break; case InExponentIndicator: if (current == '+' || current == '-') { record8(current); } else if (isDecimalDigit(current)) { record8(current); state = InExponent; } else setDone(Bad); break; case InExponent: if (isDecimalDigit(current)) { record8(current); } else setDone(Number); break; default: assert(!"Unhandled state in switch statement"); } // move on to the next character if (!done) shift(1); #ifndef KJS_PURE_ECMA if (state != Start && state != InSingleLineComment) bol = false; #endif } // no identifiers allowed directly after numeric literal, e.g. "3in" is bad if ((state == Number || state == Octal || state == Hex) && isIdentLetter(current)) state = Bad; // terminate string buffer8[pos8] = '\0'; #ifdef KJS_DEBUG_LEX fprintf(stderr, "line: %d ", lineNo()); fprintf(stderr, "yytext (%x): ", buffer8[0]); fprintf(stderr, "%s ", buffer8); #endif double dval = 0; if (state == Number) { dval = strtod(buffer8, 0L); } else if (state == Hex) { // scan hex numbers // TODO: support long unsigned int unsigned int i; sscanf(buffer8, "%x", &i); dval = i; state = Number; } else if (state == Octal) { // scan octal number unsigned int ui; sscanf(buffer8, "%o", &ui); dval = ui; state = Number; } #ifdef KJS_DEBUG_LEX switch (state) { case Eof: printf("(EOF)\n"); break; case Other: printf("(Other)\n"); break; case Identifier: printf("(Identifier)/(Keyword)\n"); break; case String: printf("(String)\n"); break; case Number: printf("(Number)\n"); break; default: printf("(unknown)"); } #endif restrKeyword = false; delimited = false; yylloc.first_line = yylineno; // ??? yylloc.last_line = yylineno; switch (state) { case Eof: return 0; case Other: if(token == '}' || token == ';') { delimited = true; } return token; case Identifier: if ((token = Lookup::find(&mainTable, buffer16, pos16)) < 0) { /* TODO: close leak on parse error. same holds true for String */ kjsyylval.ustr = new UString(buffer16, pos16); return IDENT; } if (token == CONTINUE || token == BREAK || token == RETURN || token == THROW) restrKeyword = true; return token; case String: kjsyylval.ustr = new UString(buffer16, pos16); return STRING; case Number: kjsyylval.dval = dval; return NUMBER; case Bad: fprintf(stderr, "yylex: ERROR.\n"); return -1; default: assert(!"unhandled numeration value in switch"); return -1; } } bool Lexer::isWhiteSpace() const { return (current == ' ' || current == '\t' || current == 0x0b || current == 0x0c); } bool Lexer::isLineTerminator() { bool cr = (current == '\r'); bool lf = (current == '\n'); if (cr) skipLF = true; else if (lf) skipCR = true; return cr || lf; } bool Lexer::isIdentLetter(unsigned short c) { /* TODO: allow other legitimate unicode chars */ return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '$' || c == '_'); } bool Lexer::isDecimalDigit(unsigned short c) { return (c >= '0' && c <= '9'); } bool Lexer::isHexDigit(unsigned short c) const { return (c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'); } bool Lexer::isOctalDigit(unsigned short c) const { return (c >= '0' && c <= '7'); } int Lexer::matchPunctuator(unsigned short c1, unsigned short c2, unsigned short c3, unsigned short c4) { if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { shift(4); return URSHIFTEQUAL; } else if (c1 == '=' && c2 == '=' && c3 == '=') { shift(3); return STREQ; } else if (c1 == '!' && c2 == '=' && c3 == '=') { shift(3); return STRNEQ; } else if (c1 == '>' && c2 == '>' && c3 == '>') { shift(3); return URSHIFT; } else if (c1 == '<' && c2 == '<' && c3 == '=') { shift(3); return LSHIFTEQUAL; } else if (c1 == '>' && c2 == '>' && c3 == '=') { shift(3); return RSHIFTEQUAL; } else if (c1 == '<' && c2 == '=') { shift(2); return LE; } else if (c1 == '>' && c2 == '=') { shift(2); return GE; } else if (c1 == '!' && c2 == '=') { shift(2); return NE; } else if (c1 == '+' && c2 == '+') { shift(2); if (terminator) return AUTOPLUSPLUS; else return PLUSPLUS; } else if (c1 == '-' && c2 == '-') { shift(2); if (terminator) return AUTOMINUSMINUS; else return MINUSMINUS; } else if (c1 == '=' && c2 == '=') { shift(2); return EQEQ; } else if (c1 == '+' && c2 == '=') { shift(2); return PLUSEQUAL; } else if (c1 == '-' && c2 == '=') { shift(2); return MINUSEQUAL; } else if (c1 == '*' && c2 == '=') { shift(2); return MULTEQUAL; } else if (c1 == '/' && c2 == '=') { shift(2); return DIVEQUAL; } else if (c1 == '&' && c2 == '=') { shift(2); return ANDEQUAL; } else if (c1 == '^' && c2 == '=') { shift(2); return XOREQUAL; } else if (c1 == '%' && c2 == '=') { shift(2); return MODEQUAL; } else if (c1 == '|' && c2 == '=') { shift(2); return OREQUAL; } else if (c1 == '<' && c2 == '<') { shift(2); return LSHIFT; } else if (c1 == '>' && c2 == '>') { shift(2); return RSHIFT; } else if (c1 == '&' && c2 == '&') { shift(2); return AND; } else if (c1 == '|' && c2 == '|') { shift(2); return OR; } switch(c1) { case '=': case '>': case '<': case ',': case '!': case '~': case '?': case ':': case '.': case '+': case '-': case '*': case '/': case '&': case '|': case '^': case '%': case '(': case ')': case '{': case '}': case '[': case ']': case ';': shift(1); return static_cast(c1); default: return -1; } } unsigned short Lexer::singleEscape(unsigned short c) const { switch(c) { case 'b': return 0x08; case 't': return 0x09; case 'n': return 0x0A; case 'v': return 0x0B; case 'f': return 0x0C; case 'r': return 0x0D; case '"': return 0x22; case '\'': return 0x27; case '\\': return 0x5C; default: return c; } } unsigned short Lexer::convertOctal(unsigned short c1, unsigned short c2, unsigned short c3) const { return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0'); } unsigned char Lexer::convertHex(unsigned short c) { if (c >= '0' && c <= '9') return (c - '0'); else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); else return (c - 'A' + 10); } unsigned char Lexer::convertHex(unsigned short c1, unsigned short c2) { return ((convertHex(c1) << 4) + convertHex(c2)); } UChar Lexer::convertUnicode(unsigned short c1, unsigned short c2, unsigned short c3, unsigned short c4) { return UChar((convertHex(c1) << 4) + convertHex(c2), (convertHex(c3) << 4) + convertHex(c4)); } void Lexer::record8(unsigned short c) { assert(c <= 0xff); // enlarge buffer if full if (pos8 >= size8 - 1) { char *tmp = new char[2 * size8]; memcpy(tmp, buffer8, size8 * sizeof(char)); delete [] buffer8; buffer8 = tmp; size8 *= 2; } buffer8[pos8++] = (char) c; } void Lexer::record16(UChar c) { // enlarge buffer if full if (pos16 >= size16 - 1) { UChar *tmp = new UChar[2 * size16]; memcpy(tmp, buffer16, size16 * sizeof(UChar)); delete [] buffer16; buffer16 = tmp; size16 *= 2; } buffer16[pos16++] = c; } bool Lexer::scanRegExp() { pos16 = 0; bool lastWasEscape = false; + bool inBrackets = false; while (1) { if (isLineTerminator() || current == 0) return false; - else if (current != '/' || lastWasEscape == true) + else if (current != '/' || lastWasEscape == true || inBrackets == true) { + // keep track of '[' and ']' + if ( !lastWasEscape ) { + if ( current == '[' && !inBrackets ) + inBrackets = true; + if ( current == ']' && inBrackets ) + inBrackets = false; + } record16(current); lastWasEscape = !lastWasEscape && (current == '\\'); } - else { + else { // end of regexp pattern = UString(buffer16, pos16); pos16 = 0; shift(1); break; } shift(1); } while (isIdentLetter(current)) { record16(current); shift(1); } flags = UString(buffer16, pos16); return true; } Index: trunk/kdelibs/kjs/object.cpp =================================================================== --- trunk/kdelibs/kjs/object.cpp (revision 123087) +++ trunk/kdelibs/kjs/object.cpp (revision 123088) @@ -1,666 +1,666 @@ // -*- c-basic-offset: 2 -*- /* * This file is part of the KDE libraries * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * $Id$ */ #include "value.h" #include "object.h" #include "types.h" #include "interpreter.h" #include "lookup.h" #include #include #include #include "internal.h" #include "collector.h" #include "operations.h" #include "error_object.h" #include "nodes.h" #include "property_map.h" using namespace KJS; // ------------------------------ Object --------------------------------------- Object::Object() : Value() { } Object::Object(ObjectImp *v) : Value(v) { } Object::Object(const Object &v) : Value(v) { } Object::~Object() { } Object& Object::operator=(const Object &v) { Value::operator=(v); return *this; } const ClassInfo *Object::classInfo() const { return static_cast(rep)->classInfo(); } bool Object::inherits(const ClassInfo *cinfo) const { return static_cast(rep)->inherits(cinfo); } Object Object::dynamicCast(const Value &v) { if (v.isNull() || v.type() != ObjectType) return 0; return static_cast(v.imp()); } Value Object::prototype() const { return static_cast(rep)->prototype(); } UString Object::className() const { return static_cast(rep)->className(); } Value Object::get(ExecState *exec, const UString &propertyName) const { return static_cast(rep)->get(exec,propertyName); } void Object::put(ExecState *exec, const UString &propertyName, const Value &value, int attr) { static_cast(rep)->put(exec,propertyName,value,attr); } bool Object::canPut(ExecState *exec, const UString &propertyName) const { return static_cast(rep)->canPut(exec,propertyName); } bool Object::hasProperty(ExecState *exec, const UString &propertyName, bool recursive) const { return static_cast(rep)->hasProperty(exec,propertyName,recursive); } bool Object::deleteProperty(ExecState *exec, const UString &propertyName) { return static_cast(rep)->deleteProperty(exec,propertyName); } Value Object::defaultValue(ExecState *exec, Type hint) const { return static_cast(rep)->defaultValue(exec,hint); } bool Object::implementsConstruct() const { return static_cast(rep)->implementsConstruct(); } Object Object::construct(ExecState *exec, const List &args) { return static_cast(rep)->construct(exec,args); } bool Object::implementsCall() const { return static_cast(rep)->implementsCall(); } Value Object::call(ExecState *exec, Object &thisObj, const List &args) { return static_cast(rep)->call(exec,thisObj,args); } bool Object::implementsHasInstance() const { return static_cast(rep)->implementsHasInstance(); } Boolean Object::hasInstance(ExecState *exec, const Value &value) { return static_cast(rep)->hasInstance(exec,value); } const List Object::scope() const { return static_cast(rep)->scope(); } void Object::setScope(const List &s) { static_cast(rep)->setScope(s); } List Object::propList(ExecState *exec, bool recursive) { return static_cast(rep)->propList(exec,recursive); } Value Object::internalValue() const { return static_cast(rep)->internalValue(); } void Object::setInternalValue(const Value &v) { static_cast(rep)->setInternalValue(v); } // ------------------------------ ObjectImp ------------------------------------ ObjectImp::ObjectImp(const Object &proto) : _prop(0), _proto(static_cast(proto.imp())), _internalValue(0L), _scope(0) { //fprintf(stderr,"ObjectImp::ObjectImp %p %s\n",(void*)this); _scope = ListImp::empty(); _prop = new PropertyMap(); } ObjectImp::ObjectImp() { //fprintf(stderr,"ObjectImp::ObjectImp %p %s\n",(void*)this); _prop = 0; _proto = NullImp::staticNull; _internalValue = 0L; _scope = ListImp::empty(); _prop = new PropertyMap(); } ObjectImp::~ObjectImp() { //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this); if (_proto) _proto->setGcAllowed(); if (_internalValue) _internalValue->setGcAllowed(); if (_scope) _scope->setGcAllowed(); delete _prop; } void ObjectImp::mark() { //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this); ValueImp::mark(); if (_proto && !_proto->marked()) _proto->mark(); PropertyMapNode *node = _prop->first(); while (node) { if (!node->value->marked()) node->value->mark(); node = node->next(); } if (_internalValue && !_internalValue->marked()) _internalValue->mark(); if (_scope && !_scope->marked()) _scope->mark(); } const ClassInfo *ObjectImp::classInfo() const { return 0; } bool ObjectImp::inherits(const ClassInfo *info) const { if (!info) return false; const ClassInfo *ci = classInfo(); if (!ci || !info) return false; while (ci && ci != info) ci = ci->parentClass; return (ci == info); } Type ObjectImp::type() const { return ObjectType; } Value ObjectImp::prototype() const { return _proto; } void ObjectImp::setPrototype(const Value &proto) { _proto = proto.imp(); } UString ObjectImp::className() const { const ClassInfo *ci = classInfo(); if ( ci ) return ci->className; return "Object"; } Value ObjectImp::get(ExecState *exec, const UString &propertyName) const { if (propertyName == "__proto__") { Object proto = Object::dynamicCast(prototype()); // non-standard netscape extension if (proto.isNull()) return Null(); else return proto; } ValueImp *imp = getDirect(propertyName); if ( imp ) return imp; Object proto = Object::dynamicCast(prototype()); if (proto.isNull()) return Undefined(); return proto.get(exec,propertyName); } // This get method only looks at the property map. // A bit like hasProperty(recursive=false), this doesn't go to the prototype. // This is used e.g. by lookupOrCreateFunction (to cache a function, we don't want // to look up in the prototype, it might already exist there) ValueImp* ObjectImp::getDirect(const UString& propertyName) const { return _prop->get(propertyName); } // ECMA 8.6.2.2 void ObjectImp::put(ExecState *exec, const UString &propertyName, const Value &value, int attr) { assert(!value.isNull()); assert(value.type() != ReferenceType); assert(value.type() != CompletionType); assert(value.type() != ListType); /* TODO: check for write permissions directly w/o this call */ // putValue() is used for JS assignemnts. It passes no attribute. // Assume that a C++ implementation knows what it is doing // and let it override the canPut() check. if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) { #ifdef KJS_VERBOSE - fprintf( stderr, "canPut %s said NO\n", propertyName.ascii() ); + fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() ); #endif return; } if (propertyName == "__proto__") { // non-standard netscape extension setPrototype(value); return; } _prop->put(propertyName,value.imp(),attr); } // ECMA 8.6.2.3 bool ObjectImp::canPut(ExecState *, const UString &propertyName) const { PropertyMapNode *node = _prop->getNode(propertyName); if (node) return!(node->attr & ReadOnly); // Look in the static hashtable of properties const HashEntry* e = findPropertyHashEntry(propertyName); if (e) return !(e->attr & ReadOnly); // Don't look in the prototype here. We can always put an override // in the object, even if the prototype has a ReadOnly property. return true; } // ECMA 8.6.2.4 bool ObjectImp::hasProperty(ExecState *exec, const UString &propertyName, bool recursive) const { if (propertyName == "__proto__") return true; if (_prop->get(propertyName)) return true; // Look in the static hashtable of properties if (findPropertyHashEntry(propertyName)) return true; // Look in the prototype Object proto = Object::dynamicCast(prototype()); if (proto.isNull() || !recursive) return false; return proto.hasProperty(exec,propertyName); } // ECMA 8.6.2.5 bool ObjectImp::deleteProperty(ExecState */*exec*/, const UString &propertyName) { PropertyMapNode *node = _prop->getNode(propertyName); if (node) { if ((node->attr & DontDelete)) return false; _prop->remove(propertyName); } // Look in the static hashtable of properties if (findPropertyHashEntry(propertyName)) return false; // No builtin property can be deleted return true; } // ECMA 8.6.2.6 Value ObjectImp::defaultValue(ExecState *exec, Type hint) const { if (hint != StringType && hint != NumberType) { /* Prefer String for Date objects */ if (_proto == exec->interpreter()->builtinDatePrototype().imp()) hint = StringType; else hint = NumberType; } Value v; if (hint == StringType) v = get(exec,"toString"); else v = get(exec,"valueOf"); if (v.type() == ObjectType) { Object o = static_cast(v.imp()); if (o.implementsCall()) { // spec says "not primitive type" but ... Object thisObj = Object(const_cast(this)); Value def = o.call(exec,thisObj,List::empty()); Type defType = def.type(); if (defType == UnspecifiedType || defType == UndefinedType || defType == NullType || defType == BooleanType || defType == StringType || defType == NumberType) { return def; } } } if (hint == StringType) v = get(exec,"valueOf"); else v = get(exec,"toString"); if (v.type() == ObjectType) { Object o = static_cast(v.imp()); if (o.implementsCall()) { // spec says "not primitive type" but ... Object thisObj = Object(const_cast(this)); Value def = o.call(exec,thisObj,List::empty()); Type defType = def.type(); if (defType == UnspecifiedType || defType == UndefinedType || defType == NullType || defType == BooleanType || defType == StringType || defType == NumberType) { return def; } } } Object err = Error::create(exec, TypeError, I18N_NOOP("No default value")); exec->setException(err); return err; } const HashEntry* ObjectImp::findPropertyHashEntry( const UString& propertyName ) const { const ClassInfo *info = classInfo(); while (info) { if (info->propHashTable) { const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName); if (e) return e; } info = info->parentClass; } return 0L; } bool ObjectImp::implementsConstruct() const { return false; } Object ObjectImp::construct(ExecState */*exec*/, const List &/*args*/) { assert(false); return 0; } bool ObjectImp::implementsCall() const { return false; } Value ObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/) { assert(false); return 0; } bool ObjectImp::implementsHasInstance() const { return false; } Boolean ObjectImp::hasInstance(ExecState */*exec*/, const Value &/*value*/) { assert(false); return Boolean(false); } const List ObjectImp::scope() const { return _scope; } void ObjectImp::setScope(const List &s) { _scope = static_cast(s.imp()); } List ObjectImp::propList(ExecState *exec, bool recursive) { List list; if (_proto && _proto->type() == ObjectType && recursive) list = static_cast(_proto)->propList(exec,recursive); PropertyMapNode *node = _prop->first(); while (node) { if (!(node->attr & DontEnum)) list.append(Reference(this,node->name)); node = node->next(); } // Add properties from the static hashtable of properties const ClassInfo *info = classInfo(); while (info) { if (info->propHashTable) { int size = info->propHashTable->size; const HashEntry *e = info->propHashTable->entries; for (int i = 0; i < size; ++i, ++e) { if ( e->s && !(e->attr & DontEnum) ) list.append(Reference(this,e->s)); /// ######### check for duplicates with the propertymap } } info = info->parentClass; } return list; } Value ObjectImp::internalValue() const { return _internalValue; } void ObjectImp::setInternalValue(const Value &v) { _internalValue = v.imp(); } // The following functions simply call the corresponding functions in ValueImp // but are overridden in case of future needs Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const { return defaultValue(exec,preferredType); } bool ObjectImp::toBoolean(ExecState */*exec*/) const { return true; } double ObjectImp::toNumber(ExecState *exec) const { Value prim = toPrimitive(exec,NumberType); if (exec->hadException()) // should be picked up soon in nodes.cpp return 0.0; return prim.toNumber(exec); } int ObjectImp::toInteger(ExecState *exec) const { return ValueImp::toInteger(exec); } int ObjectImp::toInt32(ExecState *exec) const { return ValueImp::toInt32(exec); } unsigned int ObjectImp::toUInt32(ExecState *exec) const { return ValueImp::toUInt32(exec); } unsigned short ObjectImp::toUInt16(ExecState *exec) const { return ValueImp::toUInt16(exec); } UString ObjectImp::toString(ExecState *exec) const { Value prim = toPrimitive(exec,StringType); if (exec->hadException()) // should be picked up soon in nodes.cpp return ""; return prim.toString(exec); } Object ObjectImp::toObject(ExecState */*exec*/) const { return Object(const_cast(this)); } // ------------------------------ Error ---------------------------------------- const char *errorNamesArr[] = { I18N_NOOP("Error"), // GeneralError I18N_NOOP("Evaluation error"), // EvalError I18N_NOOP("Range error"), // RangeError I18N_NOOP("Reference error"), // ReferenceError I18N_NOOP("Syntax error"), // SyntaxError I18N_NOOP("Type error"), // TypeError I18N_NOOP("URI error"), // URIError }; const char **Error::errorNames = errorNamesArr; Object Error::create(ExecState *exec, ErrorType errtype, const char *message, int lineno, int sourceId) { Object cons; switch (errtype) { case EvalError: cons = exec->interpreter()->builtinEvalError(); break; case RangeError: cons = exec->interpreter()->builtinRangeError(); break; case ReferenceError: cons = exec->interpreter()->builtinReferenceError(); break; case SyntaxError: cons = exec->interpreter()->builtinSyntaxError(); break; case TypeError: cons = exec->interpreter()->builtinTypeError(); break; case URIError: cons = exec->interpreter()->builtinURIError(); break; default: cons = exec->interpreter()->builtinError(); break; } if (!message) message = errorNames[errtype]; List args; args.append(String(message)); Object err = Object::dynamicCast(cons.construct(exec,args)); if (lineno != -1) err.put(exec, "line", Number(lineno)); if (sourceId != -1) err.put(exec, "sourceId", Number(sourceId)); return err; /* #ifndef NDEBUG const char *msg = err.get("message").toString().value().ascii(); if (l >= 0) fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg); else fprintf(stderr, "KJS: %s. %s\n", estr, msg); #endif return err; */ }