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