// -*- c-basic-offset: 2 -*- /* * This file is part of the KDE libraries * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2004 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include #include #include #include "array_object.h" #include "bool_object.h" #include "collector.h" #include "context.h" #include "date_object.h" #include "debugger.h" #include "error_object.h" #include "function_object.h" #include "internal.h" #include "lexer.h" #include "math_object.h" #include "nodes.h" #include "number_object.h" #include "object.h" #include "object_object.h" #include "operations.h" #include "regexp_object.h" #include "string_object.h" #define I18N_NOOP(s) s extern int kjsyyparse(); using namespace KJS; namespace KJS { /* work around some strict tqalignment requirements for double variables on some architectures (e.g. PA-RISC) */ typedef union { unsigned char b[8]; double d; } kjs_double_t; #ifdef WORDS_BIGENDIAN static const kjs_double_t NaN_Bytes = { { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 } }; static const kjs_double_t Inf_Bytes = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } }; #elif defined(arm) static const kjs_double_t NaN_Bytes = { { 0, 0, 0xf8, 0x7f, 0, 0, 0, 0 } }; static const kjs_double_t Inf_Bytes = { { 0, 0, 0xf0, 0x7f, 0, 0, 0, 0 } }; #else static const kjs_double_t NaN_Bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } }; static const kjs_double_t Inf_Bytes = { { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } }; #endif const double NaN = NaN_Bytes.d; const double Inf = Inf_Bytes.d; } #ifdef KJS_THREADSUPPORT static pthread_once_t interpreterLockOnce = PTHREAD_ONCE_INIT; static pthread_mutex_t interpreterLock; static int interpreterLockCount = 0; static void initializeInterpreterLock() { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&interpreterLock, &attr); } #endif static inline void lockInterpreter() { #ifdef KJS_THREADSUPPORT pthread_once(&interpreterLockOnce, initializeInterpreterLock); pthread_mutex_lock(&interpreterLock); interpreterLockCount++; #endif } static inline void unlockInterpreter() { #ifdef KJS_THREADSUPPORT interpreterLockCount--; pthread_mutex_unlock(&interpreterLock); #endif } // ------------------------------ UndefinedImp --------------------------------- UndefinedImp *UndefinedImp::staticUndefined = 0; Value UndefinedImp::toPrimitive(ExecState* /*exec*/, Type) const { return Value((ValueImp*)this); } bool UndefinedImp::toBoolean(ExecState* /*exec*/) const { return false; } double UndefinedImp::toNumber(ExecState* /*exec*/) const { return NaN; } UString UndefinedImp::toString(ExecState* /*exec*/) const { return "undefined"; } Object UndefinedImp::toObject(ExecState *exec) const { Object err = Error::create(exec, TypeError, I18N_NOOP("Undefined value")); exec->setException(err); return err; } // ------------------------------ NullImp -------------------------------------- NullImp *NullImp::staticNull = 0; Value NullImp::toPrimitive(ExecState* /*exec*/, Type) const { return Value((ValueImp*)this); } bool NullImp::toBoolean(ExecState* /*exec*/) const { return false; } double NullImp::toNumber(ExecState* /*exec*/) const { return 0.0; } UString NullImp::toString(ExecState* /*exec*/) const { return "null"; } Object NullImp::toObject(ExecState *exec) const { Object err = Error::create(exec, TypeError, I18N_NOOP("Null value")); exec->setException(err); return err; } // ------------------------------ BooleanImp ----------------------------------- BooleanImp* BooleanImp::staticTrue = 0; BooleanImp* BooleanImp::staticFalse = 0; Value BooleanImp::toPrimitive(ExecState* /*exec*/, Type) const { return Value((ValueImp*)this); } bool BooleanImp::toBoolean(ExecState* /*exec*/) const { return val; } double BooleanImp::toNumber(ExecState* /*exec*/) const { return val ? 1.0 : 0.0; } UString BooleanImp::toString(ExecState* /*exec*/) const { return val ? "true" : "false"; } Object BooleanImp::toObject(ExecState *exec) const { List args; args.append(const_cast(this)); return Object::dynamicCast(exec->lexicalInterpreter()->builtinBoolean().construct(exec,args)); } // ------------------------------ StringImp ------------------------------------ Value StringImp::toPrimitive(ExecState* /*exec*/, Type) const { return Value((ValueImp*)this); } bool StringImp::toBoolean(ExecState* /*exec*/) const { return (val.size() > 0); } double StringImp::toNumber(ExecState* /*exec*/) const { return val.toDouble(); } UString StringImp::toString(ExecState* /*exec*/) const { return val; } Object StringImp::toObject(ExecState *exec) const { List args; args.append(const_cast(this)); return Object(static_cast(exec->lexicalInterpreter()->builtinString().construct(exec, args).imp())); } // ------------------------------ NumberImp ------------------------------------ NumberImp *NumberImp::staticNaN; ValueImp *NumberImp::create(int i) { if (SimpleNumber::fits(i)) return SimpleNumber::make(i); NumberImp *imp = new NumberImp(static_cast(i)); imp->setGcAllowedFast(); return imp; } ValueImp *NumberImp::create(double d) { if (SimpleNumber::fits(d)) return SimpleNumber::make((int)d); if (isNaN(d)) return staticNaN; NumberImp *imp = new NumberImp(d); imp->setGcAllowedFast(); return imp; } Value NumberImp::toPrimitive(ExecState *, Type) const { return Number((NumberImp*)this); } bool NumberImp::toBoolean(ExecState *) const { return !((val == 0) /* || (iVal() == N0) */ || isNaN(val)); } double NumberImp::toNumber(ExecState *) const { return val; } UString NumberImp::toString(ExecState *) const { if (val == 0.0) // +0.0 or -0.0 return "0"; return UString::from(val); } Object NumberImp::toObject(ExecState *exec) const { List args; args.append(const_cast(this)); return Object::dynamicCast(exec->lexicalInterpreter()->builtinNumber().construct(exec,args)); } bool NumberImp::toUInt32(unsigned& uint32) const { uint32 = (unsigned)val; return (double)uint32 == val; } double SimpleNumber::negZero = -0.0; // ------------------------------ LabelStack ----------------------------------- LabelStack::LabelStack(const LabelStack &other) { tos = 0; *this = other; } LabelStack &LabelStack::operator=(const LabelStack &other) { clear(); tos = 0; StackElem *cur = 0; StackElem *se = other.tos; while (se) { StackElem *newPrev = new StackElem; newPrev->prev = 0; newPrev->id = se->id; if (cur) cur->prev = newPrev; else tos = newPrev; cur = newPrev; se = se->prev; } return *this; } bool LabelStack::push(const Identifier &id) { if (id.isEmpty() || contains(id)) return false; StackElem *newtos = new StackElem; newtos->id = id; newtos->prev = tos; tos = newtos; return true; } bool LabelStack::contains(const Identifier &id) const { if (id.isEmpty()) return true; for (StackElem *curr = tos; curr; curr = curr->prev) if (curr->id == id) return true; return false; } void LabelStack::pop() { if (tos) { StackElem *prev = tos->prev; delete tos; tos = prev; } } LabelStack::~LabelStack() { clear(); } void LabelStack::clear() { StackElem *prev; while (tos) { prev = tos->prev; delete tos; tos = prev; } } // ------------------------------ ContextImp ----------------------------------- // ECMA 10.2 ContextImp::ContextImp(Object &glob, InterpreterImp *interpreter, Object &thisV, int _sourceId, CodeType type, ContextImp *callingCon, FunctionImp *func, const List *args) : _interpreter(interpreter), _function(func), _arguments(args) { m_codeType = type; _callingContext = callingCon; tryCatch = 0; sourceId = _sourceId; line0 = 1; line1 = 1; if (func && func->inherits(&DeclaredFunctionImp::info)) functionName = static_cast(func)->name(); else functionName = Identifier::null(); // create and initialize activation object (ECMA 10.1.6) if (type == FunctionCode) { activation = Object(new ActivationImp(func,*args)); variable = activation; } else { activation = Object(); variable = glob; } // ECMA 10.2 switch(type) { case EvalCode: if (_callingContext) { scope = _callingContext->scopeChain(); #ifndef KJS_PURE_ECMA if (thisV.imp() != glob.imp()) scope.push(thisV.imp()); // for deprecated Object.prototype.eval() #endif variable = _callingContext->variableObject(); thisVal = _callingContext->thisValue(); break; } // else same as GlobalCode case GlobalCode: scope.clear(); scope.push(glob.imp()); #ifndef KJS_PURE_ECMA if (thisV.isValid()) thisVal = thisV; else #endif thisVal = glob; break; case FunctionCode: scope = func->scope(); scope.push(activation.imp()); variable = activation; // TODO: DontDelete ? (ECMA 10.2.3) thisVal = thisV; break; } _interpreter->setContext(this); } ContextImp::~ContextImp() { _interpreter->setContext(_callingContext); } void ContextImp::mark() { for (ContextImp *context = this; context; context = context->_callingContext) { context->scope.mark(); } } bool ContextImp::inTryCatch() const { const ContextImp *c = this; while (c && !c->tryCatch) c = c->_callingContext; return (c && c->tryCatch); } // ---------------------------- SourceCode ------------------------------------- void SourceCode::cleanup() { if (interpreter && interpreter->debugger()) interpreter->debugger()->sourceUnused(interpreter->globalExec(),sid); if (interpreter) interpreter->removeSourceCode(this); delete this; } // ------------------------------ Parser --------------------------------------- FunctionBodyNode *Parser::progNode = 0; int Parser::sid = 0; SourceCode *Parser::source = 0; FunctionBodyNode *Parser::parse(const UChar *code, unsigned int length, SourceCode **src, int *errLine, UString *errMsg) { if (errLine) *errLine = -1; if (errMsg) *errMsg = 0; Lexer::curr()->setCode(code, length); progNode = 0; sid++; source = new SourceCode(sid); source->ref(); *src = source; // Enable this (and the #define YYDEBUG in grammar.y) to debug a parse error //extern int kjsyydebug; //kjsyydebug=1; int parseError = kjsyyparse(); if (Lexer::curr()->hadError()) parseError = 1; Lexer::curr()->doneParsing(); FunctionBodyNode *prog = progNode; progNode = 0; //sid = -1; source = 0; if (parseError) { int eline = Lexer::curr()->lineNo(); if (errLine) *errLine = eline; if (errMsg) *errMsg = "Parse error at line " + UString::from(eline); #ifdef KJS_VERBOSE fprintf( stderr, "%s\n", UString(code,length).ascii() ); #endif #ifndef NDEBUG fprintf(stderr, "KJS: JavaScript parse error at line %d.\n", eline); #endif delete prog; return 0; } #ifdef KJS_VERBOSE fprintf( stderr, "%s\n", prog->toCode().ascii() ); #endif return prog; } // ------------------------------ InterpreterImp ------------------------------- InterpreterImp* InterpreterImp::s_hook = 0L; void InterpreterImp::globalInit() { //fprintf( stderr, "InterpreterImp::globalInit()\n" ); UndefinedImp::staticUndefined = new UndefinedImp(); UndefinedImp::staticUndefined->ref(); NullImp::staticNull = new NullImp(); NullImp::staticNull->ref(); BooleanImp::staticTrue = new BooleanImp(true); BooleanImp::staticTrue->ref(); BooleanImp::staticFalse = new BooleanImp(false); BooleanImp::staticFalse->ref(); NumberImp::staticNaN = new NumberImp(NaN); NumberImp::staticNaN->ref(); } void InterpreterImp::globalClear() { //fprintf( stderr, "InterpreterImp::globalClear()\n" ); UndefinedImp::staticUndefined->deref(); UndefinedImp::staticUndefined->setGcAllowed(); UndefinedImp::staticUndefined = 0L; NullImp::staticNull->deref(); NullImp::staticNull->setGcAllowed(); NullImp::staticNull = 0L; BooleanImp::staticTrue->deref(); BooleanImp::staticTrue->setGcAllowed(); BooleanImp::staticTrue = 0L; BooleanImp::staticFalse->deref(); BooleanImp::staticFalse->setGcAllowed(); BooleanImp::staticFalse = 0L; NumberImp::staticNaN->deref(); NumberImp::staticNaN->setGcAllowed(); NumberImp::staticNaN = 0; } InterpreterImp::InterpreterImp(Interpreter *interp, const Object &glob) : m_interpreter(interp), global(glob), dbg(0), m_compatMode(Interpreter::NativeMode), _context(0), recursion(0), sources(0) { // add this interpreter to the global chain // as a root set for garbage collection lockInterpreter(); if (s_hook) { prev = s_hook; next = s_hook->next; s_hook->next->prev = this; s_hook->next = this; } else { // This is the first interpreter s_hook = next = prev = this; globalInit(); } unlockInterpreter(); globExec = new ExecState(m_interpreter,0); // initialize properties of the global object initGlobalObject(); } void InterpreterImp::lock() { lockInterpreter(); } void InterpreterImp::unlock() { unlockInterpreter(); } void InterpreterImp::initGlobalObject() { // Contructor prototype objects (Object.prototype, Array.prototype etc) FunctionPrototypeImp *funcProto = new FunctionPrototypeImp(globExec); b_FunctionPrototype = Object(funcProto); ObjectPrototypeImp *objProto = new ObjectPrototypeImp(globExec,funcProto); b_ObjectPrototype = Object(objProto); funcProto->setPrototype(b_ObjectPrototype); ArrayPrototypeImp *arrayProto = new ArrayPrototypeImp(globExec,objProto); b_ArrayPrototype = Object(arrayProto); StringPrototypeImp *stringProto = new StringPrototypeImp(globExec,objProto); b_StringPrototype = Object(stringProto); BooleanPrototypeImp *booleanProto = new BooleanPrototypeImp(globExec,objProto,funcProto); b_BooleanPrototype = Object(booleanProto); NumberPrototypeImp *numberProto = new NumberPrototypeImp(globExec,objProto,funcProto); b_NumberPrototype = Object(numberProto); DatePrototypeImp *dateProto = new DatePrototypeImp(globExec,objProto); b_DatePrototype = Object(dateProto); RegExpPrototypeImp *regexpProto = new RegExpPrototypeImp(globExec,objProto,funcProto); b_RegExpPrototype = Object(regexpProto); ErrorPrototypeImp *errorProto = new ErrorPrototypeImp(globExec,objProto,funcProto); b_ErrorPrototype = Object(errorProto); static_cast(global.imp())->setPrototype(b_ObjectPrototype); // Constructors (Object, Array, etc.) b_Object = Object(new ObjectObjectImp(globExec, objProto, funcProto)); b_Function = Object(new FunctionObjectImp(globExec, funcProto)); b_Array = Object(new ArrayObjectImp(globExec, funcProto, arrayProto)); b_String = Object(new StringObjectImp(globExec, funcProto, stringProto)); b_Boolean = Object(new BooleanObjectImp(globExec, funcProto, booleanProto)); b_Number = Object(new NumberObjectImp(globExec, funcProto, numberProto)); b_Date = Object(new DateObjectImp(globExec, funcProto, dateProto)); b_RegExp = Object(new RegExpObjectImp(globExec, funcProto, regexpProto)); b_Error = Object(new ErrorObjectImp(globExec, funcProto, errorProto)); // Error object prototypes b_evalErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,EvalError, "EvalError","EvalError")); b_rangeErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,RangeError, "RangeError","RangeError")); b_referenceErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,ReferenceError, "ReferenceError","ReferenceError")); b_syntaxErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,SyntaxError, "SyntaxError","SyntaxError")); b_typeErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,TypeError, "TypeError","TypeError")); b_uriErrorPrototype = Object(new NativeErrorPrototypeImp(globExec,errorProto,URIError, "URIError","URIError")); // Error objects b_evalError = Object(new NativeErrorImp(globExec,funcProto,b_evalErrorPrototype)); b_rangeError = Object(new NativeErrorImp(globExec,funcProto,b_rangeErrorPrototype)); b_referenceError = Object(new NativeErrorImp(globExec,funcProto,b_referenceErrorPrototype)); b_syntaxError = Object(new NativeErrorImp(globExec,funcProto,b_syntaxErrorPrototype)); b_typeError = Object(new NativeErrorImp(globExec,funcProto,b_typeErrorPrototype)); b_uriError = Object(new NativeErrorImp(globExec,funcProto,b_uriErrorPrototype)); // ECMA 15.3.4.1 funcProto->put(globExec,constructorPropertyName, b_Function, DontEnum); global.put(globExec,"Object", b_Object, DontEnum); global.put(globExec,"Function", b_Function, DontEnum); global.put(globExec,"Array", b_Array, DontEnum); global.put(globExec,"Boolean", b_Boolean, DontEnum); global.put(globExec,"String", b_String, DontEnum); global.put(globExec,"Number", b_Number, DontEnum); global.put(globExec,"Date", b_Date, DontEnum); global.put(globExec,"RegExp", b_RegExp, DontEnum); global.put(globExec,"Error", b_Error, DontEnum); // Using Internal for those to have something != 0 // (see kjs_window). Maybe DontEnum would be ok too ? global.put(globExec,"EvalError",b_evalError, Internal); global.put(globExec,"RangeError",b_rangeError, Internal); global.put(globExec,"ReferenceError",b_referenceError, Internal); global.put(globExec,"SyntaxError",b_syntaxError, Internal); global.put(globExec,"TypeError",b_typeError, Internal); global.put(globExec,"URIError",b_uriError, Internal); // Set the "constructor" property of all builtin constructors objProto->put(globExec, constructorPropertyName, b_Object, DontEnum | DontDelete | ReadOnly); funcProto->put(globExec, constructorPropertyName, b_Function, DontEnum | DontDelete | ReadOnly); arrayProto->put(globExec, constructorPropertyName, b_Array, DontEnum | DontDelete | ReadOnly); booleanProto->put(globExec, constructorPropertyName, b_Boolean, DontEnum | DontDelete | ReadOnly); stringProto->put(globExec, constructorPropertyName, b_String, DontEnum | DontDelete | ReadOnly); numberProto->put(globExec, constructorPropertyName, b_Number, DontEnum | DontDelete | ReadOnly); dateProto->put(globExec, constructorPropertyName, b_Date, DontEnum | DontDelete | ReadOnly); regexpProto->put(globExec, constructorPropertyName, b_RegExp, DontEnum | DontDelete | ReadOnly); errorProto->put(globExec, constructorPropertyName, b_Error, DontEnum | DontDelete | ReadOnly); b_evalErrorPrototype.put(globExec, constructorPropertyName, b_evalError, DontEnum | DontDelete | ReadOnly); b_rangeErrorPrototype.put(globExec, constructorPropertyName, b_rangeError, DontEnum | DontDelete | ReadOnly); b_referenceErrorPrototype.put(globExec, constructorPropertyName, b_referenceError, DontEnum | DontDelete | ReadOnly); b_syntaxErrorPrototype.put(globExec, constructorPropertyName, b_syntaxError, DontEnum | DontDelete | ReadOnly); b_typeErrorPrototype.put(globExec, constructorPropertyName, b_typeError, DontEnum | DontDelete | ReadOnly); b_uriErrorPrototype.put(globExec, constructorPropertyName, b_uriError, DontEnum | DontDelete | ReadOnly); // built-in values global.put(globExec, "NaN", Number(NaN), DontEnum|DontDelete); global.put(globExec, "Infinity", Number(Inf), DontEnum|DontDelete); global.put(globExec, "undefined", Undefined(), DontEnum|DontDelete); // built-in functions #ifdef KJS_PURE_ECMA // otherwise as deprecated Object.prototype property global.put(globExec,"eval", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::Eval,1,"eval")), DontEnum); #endif global.put(globExec,"parseInt", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::ParseInt,2,"parseInt")), DontEnum); global.put(globExec,"parseFloat", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::ParseFloat,1,"parseFloat")), DontEnum); global.put(globExec,"isNaN", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::IsNaN,1,"isNaN")), DontEnum); global.put(globExec,"isFinite", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::IsFinite,1,"isFinite")), DontEnum); global.put(globExec,"decodeURI", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::DecodeURI,1,"decodeURI")), DontEnum); global.put(globExec,"decodeURIComponent", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::DecodeURIComponent,1,"decodeURIComponent")), DontEnum); global.put(globExec,"encodeURI", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::EncodeURI,1,"encodeURI")), DontEnum); global.put(globExec,"encodeURIComponent", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::EncodeURIComponent,1,"encodeURIComponent")), DontEnum); global.put(globExec,"escape", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::Escape,1,"escape")), DontEnum); global.put(globExec,"unescape", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::UnEscape,1,"unescape")), DontEnum); #ifndef NDEBUG global.put(globExec,"kjsprint", Object(new GlobalFuncImp(globExec,funcProto,GlobalFuncImp::KJSPrint,1,"kjsprint")), DontEnum); #endif // built-in objects global.put(globExec,"Math", Object(new MathObjectImp(globExec,objProto)), DontEnum); } InterpreterImp::~InterpreterImp() { if (dbg) dbg->detach(m_interpreter); for (SourceCode *s = sources; s; s = s->next) s->interpreter = 0; delete globExec; globExec = 0L; clear(); } void InterpreterImp::clear() { //fprintf(stderr,"InterpreterImp::clear\n"); // remove from global chain (see init()) lockInterpreter(); next->prev = prev; prev->next = next; s_hook = next; if (s_hook == this) { // This was the last interpreter s_hook = 0L; globalClear(); } unlockInterpreter(); } void InterpreterImp::mark() { //if (exVal && !exVal->marked()) // exVal->mark(); //if (retVal && !retVal->marked()) // retVal->mark(); if (UndefinedImp::staticUndefined && !UndefinedImp::staticUndefined->marked()) UndefinedImp::staticUndefined->mark(); if (NullImp::staticNull && !NullImp::staticNull->marked()) NullImp::staticNull->mark(); if (NumberImp::staticNaN && !NumberImp::staticNaN->marked()) NumberImp::staticNaN->mark(); if (BooleanImp::staticTrue && !BooleanImp::staticTrue->marked()) BooleanImp::staticTrue->mark(); if (BooleanImp::staticFalse && !BooleanImp::staticFalse->marked()) BooleanImp::staticFalse->mark(); //fprintf( stderr, "InterpreterImp::mark this=%p global.imp()=%p\n", this, global.imp() ); if (global.imp()) global.imp()->mark(); if (m_interpreter) m_interpreter->mark(); if (_context) _context->mark(); } bool InterpreterImp::checkSyntax(const UString &code, int *errLine, UString *errMsg) { // Parser::parse() returns 0 in a syntax error occurs, so we just check for that SourceCode *source; FunctionBodyNode *progNode = Parser::parse(code.data(),code.size(),&source,errLine,errMsg); source->deref(); bool ok = (progNode != 0); delete progNode; return ok; } bool InterpreterImp::checkSyntax(const UString &code) { // Parser::parse() returns 0 in a syntax error occurs, so we just check for that SourceCode *source; FunctionBodyNode *progNode = Parser::parse(code.data(),code.size(),&source,0,0); source->deref(); bool ok = (progNode != 0); delete progNode; return ok; } Completion InterpreterImp::evaluate(const UString &code, const Value &thisV) { lockInterpreter(); // prevent against infinite recursion if (recursion >= 20) { Completion result = Completion(Throw,Error::create(globExec,GeneralError,"Recursion too deep")); unlockInterpreter(); return result; } // parse the source code int errLine; UString errMsg; SourceCode *source; FunctionBodyNode *progNode = Parser::parse(code.data(),code.size(),&source,&errLine,&errMsg); // notify debugger that source has been parsed if (dbg) { bool cont = dbg->sourceParsed(globExec,source->sid,code,errLine); if (!cont) { source->deref(); if (progNode) delete progNode; unlockInterpreter(); return Completion(Break); } } addSourceCode(source); // no program node means a syntax error occurred if (!progNode) { Object err = Error::create(globExec,SyntaxError,errMsg.ascii(),errLine); err.put(globExec,"sid",Number(source->sid)); globExec->setException(err); // required to notify the debugger globExec->clearException(); source->deref(); unlockInterpreter(); return Completion(Throw,err); } source->deref(); globExec->clearException(); recursion++; progNode->ref(); Object &globalObj = globalObject(); Object thisObj = globalObject(); if (thisV.isValid()) { // "this" must be an object... use same rules as Function.prototype.apply() if (thisV.isA(NullType) || thisV.isA(UndefinedType)) thisObj = globalObject(); else { thisObj = thisV.toObject(globExec); } } Completion res; if (globExec->hadException()) { // the thisArg.toObject() conversion above might have thrown an exception - if so, // propagate it back res = Completion(Throw,globExec->exception()); } else { // execute the code ContextImp ctx(globalObj, this, thisObj, source->sid); ExecState newExec(m_interpreter,&ctx); // create variables (initialized to undefined until var statements // with optional initializers are executed) progNode->processVarDecls(&newExec); ctx.setLines(progNode->firstLine(),progNode->firstLine()); bool abort = false; if (dbg) { if (!dbg->enterContext(&newExec)) { // debugger requested we stop execution dbg->imp()->abort(); abort = true; } } if (!abort) { ctx.setLines(progNode->lastLine(),progNode->lastLine()); res = progNode->execute(&newExec); if (dbg && !dbg->exitContext(&newExec,res)) { // debugger requested we stop execution dbg->imp()->abort(); unlockInterpreter(); res = Completion(ReturnValue,Undefined()); } } } if (progNode->deref()) delete progNode; recursion--; if (globExec->hadException()) { res = Completion(Throw,globExec->exception()); globExec->clearException(); } unlockInterpreter(); return res; } void InterpreterImp::setDebugger(Debugger *d) { if (d == dbg) return; // avoid recursion Debugger *old = dbg; dbg = d; if ( old ) old->detach(m_interpreter); } void InterpreterImp::addSourceCode(SourceCode *code) { assert(!code->next); assert(!code->interpreter); code->next = sources; code->interpreter = this; sources = code; } void InterpreterImp::removeSourceCode(SourceCode *code) { assert(code); assert(sources); if (code == sources) { sources = sources->next; return; } SourceCode *prev = sources; SourceCode *cur = sources->next; while (cur != code) { assert(cur); prev = cur; cur = cur->next; } prev->next = cur->next; } // ------------------------------ InternalFunctionImp -------------------------- const ClassInfo InternalFunctionImp::info = {"Function", 0, 0, 0}; InternalFunctionImp::InternalFunctionImp(FunctionPrototypeImp *funcProto) : ObjectImp(funcProto) { } InternalFunctionImp::InternalFunctionImp(ExecState *exec) : ObjectImp(static_cast(exec->interpreter()->builtinFunctionPrototype().imp())) { } bool InternalFunctionImp::implementsHasInstance() const { return true; } Boolean InternalFunctionImp::hasInstance(ExecState *exec, const Value &value) { if (value.type() != ObjectType) return Boolean(false); Value prot = get(exec,prototypePropertyName); if (prot.type() != ObjectType && prot.type() != NullType) { Object err = Error::create(exec, TypeError, "Invalid prototype encountered " "in instanceof operation."); exec->setException(err); return Boolean(false); } Object v = Object(static_cast(value.imp())); while ((v = Object::dynamicCast(v.prototype())).imp()) { if (v.imp() == prot.imp()) return Boolean(true); } return Boolean(false); } // ------------------------------ global functions ----------------------------- double KJS::roundValue(ExecState *exec, const Value &v) { double n = v.toNumber(exec); if (isNaN(n) || isInf(n)) return n; double an = fabs(n); if (an == 0.0) return n; double d = floor(an); if (n < 0) d *= -1; return d; } #ifndef NDEBUG #include void KJS::printInfo(ExecState *exec, const char *s, const Value &o, int lineno) { if (!o.isValid()) fprintf(stderr, "KJS: %s: (null)", s); else { Value v = o; unsigned int arrayLength = 0; bool hadExcep = exec->hadException(); UString name; switch ( v.type() ) { case UnspecifiedType: name = "Unspecified"; break; case UndefinedType: name = "Undefined"; break; case NullType: name = "Null"; break; case BooleanType: name = "Boolean"; break; case StringType: name = "String"; break; case NumberType: name = "Number"; break; case ObjectType: { Object obj = Object::dynamicCast(v); name = obj.className(); if (name.isNull()) name = "(unknown class)"; if ( obj.inherits(&ArrayInstanceImp::info) ) arrayLength = obj.get(exec,lengthPropertyName).toUInt32(exec); } break; } UString vString; // Avoid calling toString on a huge array (e.g. 4 billion elements, in mozilla/js/js1_5/Array/array-001.js) if ( arrayLength > 100 ) vString = UString( "[ Array with " ) + UString::from( arrayLength ) + " elements ]"; else vString = v.toString(exec); if ( !hadExcep ) exec->clearException(); if ( vString.size() > 50 ) vString = vString.substr( 0, 50 ) + "..."; // Can't use two UString::ascii() in the same fprintf call CString tempString( vString.cstring() ); fprintf(stderr, "KJS: %s: %s : %s (%p)", s, tempString.c_str(), name.ascii(), (void*)v.imp()); if (lineno >= 0) fprintf(stderr, ", line %d\n",lineno); else fprintf(stderr, "\n"); } } #endif