/* * Copyright (C) 2007 Eric Seidel <eric@webkit.org> * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef JSGlobalObject_h #define JSGlobalObject_h #include "JSArray.h" #include "JSGlobalData.h" #include "JSVariableObject.h" #include "NativeFunctionWrapper.h" #include "NumberPrototype.h" #include "StringPrototype.h" #include <wtf/HashSet.h> #include <wtf/OwnPtr.h> namespace JSC { class ArrayPrototype; class BooleanPrototype; class DatePrototype; class Debugger; class ErrorConstructor; class FunctionPrototype; class GlobalCodeBlock; class GlobalEvalFunction; class NativeErrorConstructor; class ProgramCodeBlock; class PrototypeFunction; class RegExpConstructor; class RegExpPrototype; class RegisterFile; struct ActivationStackNode; struct HashTable; typedef Vector<ExecState*, 16> ExecStateStack; class JSGlobalObject : public JSVariableObject { protected: using JSVariableObject::JSVariableObjectData; struct JSGlobalObjectData : public JSVariableObjectData { // We use an explicit destructor function pointer instead of a // virtual destructor because we want to avoid adding a vtable // pointer to this struct. Adding a vtable pointer would force the // compiler to emit costly pointer fixup code when casting from // JSVariableObjectData* to JSGlobalObjectData*. typedef void (*Destructor)(void*); JSGlobalObjectData(Destructor destructor) : JSVariableObjectData(&symbolTable, 0) , destructor(destructor) , registerArraySize(0) , globalScopeChain(NoScopeChain()) , regExpConstructor(0) , errorConstructor(0) , evalErrorConstructor(0) , rangeErrorConstructor(0) , referenceErrorConstructor(0) , syntaxErrorConstructor(0) , typeErrorConstructor(0) , URIErrorConstructor(0) , evalFunction(0) , callFunction(0) , applyFunction(0) , objectPrototype(0) , functionPrototype(0) , arrayPrototype(0) , booleanPrototype(0) , stringPrototype(0) , numberPrototype(0) , datePrototype(0) , regExpPrototype(0) , methodCallDummy(0) { } Destructor destructor; size_t registerArraySize; JSGlobalObject* next; JSGlobalObject* prev; Debugger* debugger; ScopeChain globalScopeChain; Register globalCallFrame[RegisterFile::CallFrameHeaderSize]; int recursion; RegExpConstructor* regExpConstructor; ErrorConstructor* errorConstructor; NativeErrorConstructor* evalErrorConstructor; NativeErrorConstructor* rangeErrorConstructor; NativeErrorConstructor* referenceErrorConstructor; NativeErrorConstructor* syntaxErrorConstructor; NativeErrorConstructor* typeErrorConstructor; NativeErrorConstructor* URIErrorConstructor; GlobalEvalFunction* evalFunction; NativeFunctionWrapper* callFunction; NativeFunctionWrapper* applyFunction; ObjectPrototype* objectPrototype; FunctionPrototype* functionPrototype; ArrayPrototype* arrayPrototype; BooleanPrototype* booleanPrototype; StringPrototype* stringPrototype; NumberPrototype* numberPrototype; DatePrototype* datePrototype; RegExpPrototype* regExpPrototype; JSObject* methodCallDummy; RefPtr<Structure> argumentsStructure; RefPtr<Structure> arrayStructure; RefPtr<Structure> booleanObjectStructure; RefPtr<Structure> callbackConstructorStructure; RefPtr<Structure> callbackFunctionStructure; RefPtr<Structure> callbackObjectStructure; RefPtr<Structure> dateStructure; RefPtr<Structure> emptyObjectStructure; RefPtr<Structure> errorStructure; RefPtr<Structure> functionStructure; RefPtr<Structure> numberObjectStructure; RefPtr<Structure> prototypeFunctionStructure; RefPtr<Structure> regExpMatchesArrayStructure; RefPtr<Structure> regExpStructure; RefPtr<Structure> stringObjectStructure; SymbolTable symbolTable; unsigned profileGroup; RefPtr<JSGlobalData> globalData; HashSet<GlobalCodeBlock*> codeBlocks; }; public: void* operator new(size_t, JSGlobalData*); explicit JSGlobalObject() : JSVariableObject(JSGlobalObject::createStructure(jsNull()), new JSGlobalObjectData(destroyJSGlobalObjectData)) { init(this); } protected: JSGlobalObject(NonNullPassRefPtr<Structure> structure, JSGlobalObjectData* data, JSObject* thisValue) : JSVariableObject(structure, data) { init(thisValue); } public: virtual ~JSGlobalObject(); virtual void markChildren(MarkStack&); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual bool hasOwnPropertyForWrite(ExecState*, const Identifier&); virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes); virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes); // Linked list of all global objects that use the same JSGlobalData. JSGlobalObject*& head() { return d()->globalData->head; } JSGlobalObject* next() { return d()->next; } // The following accessors return pristine values, even if a script // replaces the global object's associated property. RegExpConstructor* regExpConstructor() const { return d()->regExpConstructor; } ErrorConstructor* errorConstructor() const { return d()->errorConstructor; } NativeErrorConstructor* evalErrorConstructor() const { return d()->evalErrorConstructor; } NativeErrorConstructor* rangeErrorConstructor() const { return d()->rangeErrorConstructor; } NativeErrorConstructor* referenceErrorConstructor() const { return d()->referenceErrorConstructor; } NativeErrorConstructor* syntaxErrorConstructor() const { return d()->syntaxErrorConstructor; } NativeErrorConstructor* typeErrorConstructor() const { return d()->typeErrorConstructor; } NativeErrorConstructor* URIErrorConstructor() const { return d()->URIErrorConstructor; } GlobalEvalFunction* evalFunction() const { return d()->evalFunction; } ObjectPrototype* objectPrototype() const { return d()->objectPrototype; } FunctionPrototype* functionPrototype() const { return d()->functionPrototype; } ArrayPrototype* arrayPrototype() const { return d()->arrayPrototype; } BooleanPrototype* booleanPrototype() const { return d()->booleanPrototype; } StringPrototype* stringPrototype() const { return d()->stringPrototype; } NumberPrototype* numberPrototype() const { return d()->numberPrototype; } DatePrototype* datePrototype() const { return d()->datePrototype; } RegExpPrototype* regExpPrototype() const { return d()->regExpPrototype; } JSObject* methodCallDummy() const { return d()->methodCallDummy; } Structure* argumentsStructure() const { return d()->argumentsStructure.get(); } Structure* arrayStructure() const { return d()->arrayStructure.get(); } Structure* booleanObjectStructure() const { return d()->booleanObjectStructure.get(); } Structure* callbackConstructorStructure() const { return d()->callbackConstructorStructure.get(); } Structure* callbackFunctionStructure() const { return d()->callbackFunctionStructure.get(); } Structure* callbackObjectStructure() const { return d()->callbackObjectStructure.get(); } Structure* dateStructure() const { return d()->dateStructure.get(); } Structure* emptyObjectStructure() const { return d()->emptyObjectStructure.get(); } Structure* errorStructure() const { return d()->errorStructure.get(); } Structure* functionStructure() const { return d()->functionStructure.get(); } Structure* numberObjectStructure() const { return d()->numberObjectStructure.get(); } Structure* prototypeFunctionStructure() const { return d()->prototypeFunctionStructure.get(); } Structure* regExpMatchesArrayStructure() const { return d()->regExpMatchesArrayStructure.get(); } Structure* regExpStructure() const { return d()->regExpStructure.get(); } Structure* stringObjectStructure() const { return d()->stringObjectStructure.get(); } void setProfileGroup(unsigned value) { d()->profileGroup = value; } unsigned profileGroup() const { return d()->profileGroup; } Debugger* debugger() const { return d()->debugger; } void setDebugger(Debugger* debugger) { d()->debugger = debugger; } virtual bool supportsProfiling() const { return false; } int recursion() { return d()->recursion; } void incRecursion() { ++d()->recursion; } void decRecursion() { --d()->recursion; } ScopeChain& globalScopeChain() { return d()->globalScopeChain; } virtual bool isGlobalObject() const { return true; } virtual ExecState* globalExec(); virtual bool shouldInterruptScript() const { return true; } virtual bool allowsAccessFrom(const JSGlobalObject*) const { return true; } virtual bool isDynamicScope() const; HashSet<GlobalCodeBlock*>& codeBlocks() { return d()->codeBlocks; } void copyGlobalsFrom(RegisterFile&); void copyGlobalsTo(RegisterFile&); void resetPrototype(JSValue prototype); JSGlobalData* globalData() { return d()->globalData.get(); } JSGlobalObjectData* d() const { return static_cast<JSGlobalObjectData*>(JSVariableObject::d); } static PassRefPtr<Structure> createStructure(JSValue prototype) { return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); } protected: static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; struct GlobalPropertyInfo { GlobalPropertyInfo(const Identifier& i, JSValue v, unsigned a) : identifier(i) , value(v) , attributes(a) { } const Identifier identifier; JSValue value; unsigned attributes; }; void addStaticGlobals(GlobalPropertyInfo*, int count); private: static void destroyJSGlobalObjectData(void*); // FIXME: Fold reset into init. void init(JSObject* thisValue); void reset(JSValue prototype); void setRegisters(Register* registers, Register* registerArray, size_t count); void* operator new(size_t); // can only be allocated with JSGlobalData }; JSGlobalObject* asGlobalObject(JSValue); inline JSGlobalObject* asGlobalObject(JSValue value) { ASSERT(asObject(value)->isGlobalObject()); return static_cast<JSGlobalObject*>(asObject(value)); } inline void JSGlobalObject::setRegisters(Register* registers, Register* registerArray, size_t count) { JSVariableObject::setRegisters(registers, registerArray); d()->registerArraySize = count; } inline void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count) { size_t oldSize = d()->registerArraySize; size_t newSize = oldSize + count; Register* registerArray = new Register[newSize]; if (d()->registerArray) memcpy(registerArray + count, d()->registerArray.get(), oldSize * sizeof(Register)); setRegisters(registerArray + newSize, registerArray, newSize); for (int i = 0, index = -static_cast<int>(oldSize) - 1; i < count; ++i, --index) { GlobalPropertyInfo& global = globals[i]; ASSERT(global.attributes & DontDelete); SymbolTableEntry newEntry(index, global.attributes); symbolTable().add(global.identifier.ustring().rep(), newEntry); registerAt(index) = global.value; } } inline bool JSGlobalObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (JSVariableObject::getOwnPropertySlot(exec, propertyName, slot)) return true; return symbolTableGet(propertyName, slot); } inline bool JSGlobalObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { if (symbolTableGet(propertyName, descriptor)) return true; return JSVariableObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); } inline bool JSGlobalObject::hasOwnPropertyForWrite(ExecState* exec, const Identifier& propertyName) { PropertySlot slot; if (JSVariableObject::getOwnPropertySlot(exec, propertyName, slot)) return true; bool slotIsWriteable; return symbolTableGet(propertyName, slot, slotIsWriteable); } inline JSValue Structure::prototypeForLookup(ExecState* exec) const { if (typeInfo().type() == ObjectType) return m_prototype; #if USE(JSVALUE32) if (typeInfo().type() == StringType) return exec->lexicalGlobalObject()->stringPrototype(); ASSERT(typeInfo().type() == NumberType); return exec->lexicalGlobalObject()->numberPrototype(); #else ASSERT(typeInfo().type() == StringType); return exec->lexicalGlobalObject()->stringPrototype(); #endif } inline StructureChain* Structure::prototypeChain(ExecState* exec) const { // We cache our prototype chain so our clients can share it. if (!isValid(exec, m_cachedPrototypeChain.get())) { JSValue prototype = prototypeForLookup(exec); m_cachedPrototypeChain = StructureChain::create(prototype.isNull() ? 0 : asObject(prototype)->structure()); } return m_cachedPrototypeChain.get(); } inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const { if (!cachedPrototypeChain) return false; JSValue prototype = prototypeForLookup(exec); RefPtr<Structure>* cachedStructure = cachedPrototypeChain->head(); while(*cachedStructure && !prototype.isNull()) { if (asObject(prototype)->structure() != *cachedStructure) return false; ++cachedStructure; prototype = asObject(prototype)->prototype(); } return prototype.isNull() && !*cachedStructure; } inline JSGlobalObject* ExecState::dynamicGlobalObject() { if (this == lexicalGlobalObject()->globalExec()) return lexicalGlobalObject(); // For any ExecState that's not a globalExec, the // dynamic global object must be set since code is running ASSERT(globalData().dynamicGlobalObject); return globalData().dynamicGlobalObject; } inline JSObject* constructEmptyObject(ExecState* exec) { return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); } inline JSObject* constructEmptyObject(ExecState* exec, JSGlobalObject* globalObject) { return new (exec) JSObject(globalObject->emptyObjectStructure()); } inline JSArray* constructEmptyArray(ExecState* exec) { return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure()); } inline JSArray* constructEmptyArray(ExecState* exec, JSGlobalObject* globalObject) { return new (exec) JSArray(globalObject->arrayStructure()); } inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength) { return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength); } inline JSArray* constructArray(ExecState* exec, JSValue singleItemValue) { MarkedArgumentBuffer values; values.append(singleItemValue); return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values); } inline JSArray* constructArray(ExecState* exec, const ArgList& values) { return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values); } class DynamicGlobalObjectScope : public Noncopyable { public: DynamicGlobalObjectScope(CallFrame* callFrame, JSGlobalObject* dynamicGlobalObject) : m_dynamicGlobalObjectSlot(callFrame->globalData().dynamicGlobalObject) , m_savedDynamicGlobalObject(m_dynamicGlobalObjectSlot) { if (!m_dynamicGlobalObjectSlot) { m_dynamicGlobalObjectSlot = dynamicGlobalObject; // Reset the date cache between JS invocations to force the VM // to observe time zone changes. callFrame->globalData().resetDateCache(); } } ~DynamicGlobalObjectScope() { m_dynamicGlobalObjectSlot = m_savedDynamicGlobalObject; } private: JSGlobalObject*& m_dynamicGlobalObjectSlot; JSGlobalObject* m_savedDynamicGlobalObject; }; } // namespace JSC #endif // JSGlobalObject_h