1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "JSGlobalData.h"
31
32#include "ArgList.h"
33#include "Heap.h"
34#include "CommonIdentifiers.h"
35#include "FunctionConstructor.h"
36#include "GetterSetter.h"
37#include "Interpreter.h"
38#include "JSActivation.h"
39#include "JSAPIValueWrapper.h"
40#include "JSArray.h"
41#include "JSByteArray.h"
42#include "JSClassRef.h"
43#include "JSFunction.h"
44#include "JSLock.h"
45#include "JSNotAnObject.h"
46#include "JSPropertyNameIterator.h"
47#include "JSStaticScopeObject.h"
48#include "JSZombie.h"
49#include "Lexer.h"
50#include "Lookup.h"
51#include "Nodes.h"
52#include "Parser.h"
53#include "RegExpCache.h"
54#include "StrictEvalActivation.h"
55#include <wtf/WTFThreadData.h>
56#if ENABLE(REGEXP_TRACING)
57#include "RegExp.h"
58#endif
59
60
61#if ENABLE(JSC_MULTIPLE_THREADS)
62#include <wtf/Threading.h>
63#endif
64
65#if PLATFORM(MAC)
66#include "ProfilerServer.h"
67#include <CoreFoundation/CoreFoundation.h>
68#endif
69
70using namespace WTF;
71
72namespace {
73
74using namespace JSC;
75
76class Recompiler {
77public:
78    void operator()(JSCell*);
79};
80
81inline void Recompiler::operator()(JSCell* cell)
82{
83    if (!cell->inherits(&JSFunction::s_info))
84        return;
85    JSFunction* function = asFunction(cell);
86    if (function->executable()->isHostFunction())
87        return;
88    function->jsExecutable()->discardCode();
89}
90
91} // namespace
92
93namespace JSC {
94
95extern JSC_CONST_HASHTABLE HashTable arrayTable;
96extern JSC_CONST_HASHTABLE HashTable jsonTable;
97extern JSC_CONST_HASHTABLE HashTable dateTable;
98extern JSC_CONST_HASHTABLE HashTable mathTable;
99extern JSC_CONST_HASHTABLE HashTable numberTable;
100extern JSC_CONST_HASHTABLE HashTable objectConstructorTable;
101extern JSC_CONST_HASHTABLE HashTable regExpTable;
102extern JSC_CONST_HASHTABLE HashTable regExpConstructorTable;
103extern JSC_CONST_HASHTABLE HashTable stringTable;
104
105void* JSGlobalData::jsArrayVPtr;
106void* JSGlobalData::jsByteArrayVPtr;
107void* JSGlobalData::jsStringVPtr;
108void* JSGlobalData::jsFunctionVPtr;
109
110#if COMPILER(GCC)
111// Work around for gcc trying to coalesce our reads of the various cell vptrs
112#define CLOBBER_MEMORY() do { \
113    asm volatile ("" : : : "memory"); \
114} while (false)
115#else
116#define CLOBBER_MEMORY() do { } while (false)
117#endif
118
119void JSGlobalData::storeVPtrs()
120{
121    // Enough storage to fit a JSArray, JSByteArray, JSString, or JSFunction.
122    // COMPILE_ASSERTS below check that this is true.
123    char storage[64];
124
125    COMPILE_ASSERT(sizeof(JSArray) <= sizeof(storage), sizeof_JSArray_must_be_less_than_storage);
126    JSCell* jsArray = new (storage) JSArray(JSArray::VPtrStealingHack);
127    CLOBBER_MEMORY();
128    JSGlobalData::jsArrayVPtr = jsArray->vptr();
129
130    COMPILE_ASSERT(sizeof(JSByteArray) <= sizeof(storage), sizeof_JSByteArray_must_be_less_than_storage);
131    JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack);
132    CLOBBER_MEMORY();
133    JSGlobalData::jsByteArrayVPtr = jsByteArray->vptr();
134
135    COMPILE_ASSERT(sizeof(JSString) <= sizeof(storage), sizeof_JSString_must_be_less_than_storage);
136    JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack);
137    CLOBBER_MEMORY();
138    JSGlobalData::jsStringVPtr = jsString->vptr();
139
140    COMPILE_ASSERT(sizeof(JSFunction) <= sizeof(storage), sizeof_JSFunction_must_be_less_than_storage);
141    JSCell* jsFunction = new (storage) JSFunction(JSCell::VPtrStealingHack);
142    CLOBBER_MEMORY();
143    JSGlobalData::jsFunctionVPtr = jsFunction->vptr();
144}
145
146JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType)
147    : globalDataType(globalDataType)
148    , clientData(0)
149    , arrayTable(fastNew<HashTable>(JSC::arrayTable))
150    , dateTable(fastNew<HashTable>(JSC::dateTable))
151    , jsonTable(fastNew<HashTable>(JSC::jsonTable))
152    , mathTable(fastNew<HashTable>(JSC::mathTable))
153    , numberTable(fastNew<HashTable>(JSC::numberTable))
154    , objectConstructorTable(fastNew<HashTable>(JSC::objectConstructorTable))
155    , regExpTable(fastNew<HashTable>(JSC::regExpTable))
156    , regExpConstructorTable(fastNew<HashTable>(JSC::regExpConstructorTable))
157    , stringTable(fastNew<HashTable>(JSC::stringTable))
158    , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable())
159    , propertyNames(new CommonIdentifiers(this))
160    , emptyList(new MarkedArgumentBuffer)
161    , lexer(new Lexer(this))
162    , parser(new Parser)
163    , interpreter(0)
164    , heap(this)
165    , globalObjectCount(0)
166    , dynamicGlobalObject(0)
167    , cachedUTCOffset(NaN)
168    , maxReentryDepth(threadStackType == ThreadStackTypeSmall ? MaxSmallThreadReentryDepth : MaxLargeThreadReentryDepth)
169    , m_regExpCache(new RegExpCache(this))
170#if ENABLE(REGEXP_TRACING)
171    , m_rtTraceList(new RTTraceList())
172#endif
173#ifndef NDEBUG
174    , exclusiveThread(0)
175#endif
176{
177    interpreter = new Interpreter(*this);
178    if (globalDataType == Default)
179        m_stack = wtfThreadData().stack();
180
181    // Need to be careful to keep everything consistent here
182    IdentifierTable* existingEntryIdentifierTable = wtfThreadData().setCurrentIdentifierTable(identifierTable);
183    JSLock lock(SilenceAssertionsOnly);
184    structureStructure.set(*this, Structure::createStructure(*this));
185    activationStructure.set(*this, JSActivation::createStructure(*this, jsNull()));
186    interruptedExecutionErrorStructure.set(*this, JSNonFinalObject::createStructure(*this, jsNull()));
187    terminatedExecutionErrorStructure.set(*this, JSNonFinalObject::createStructure(*this, jsNull()));
188    staticScopeStructure.set(*this, JSStaticScopeObject::createStructure(*this, jsNull()));
189    strictEvalActivationStructure.set(*this, StrictEvalActivation::createStructure(*this, jsNull()));
190    stringStructure.set(*this, JSString::createStructure(*this, jsNull()));
191    notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, jsNull()));
192    propertyNameIteratorStructure.set(*this, JSPropertyNameIterator::createStructure(*this, jsNull()));
193    getterSetterStructure.set(*this, GetterSetter::createStructure(*this, jsNull()));
194    apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, jsNull()));
195    scopeChainNodeStructure.set(*this, ScopeChainNode::createStructure(*this, jsNull()));
196    executableStructure.set(*this, ExecutableBase::createStructure(*this, jsNull()));
197    nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, jsNull()));
198    evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, jsNull()));
199    programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, jsNull()));
200    functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, jsNull()));
201    dummyMarkableCellStructure.set(*this, JSCell::createDummyStructure(*this));
202    structureChainStructure.set(*this, StructureChain::createStructure(*this, jsNull()));
203
204#if ENABLE(JSC_ZOMBIES)
205    zombieStructure.set(*this, JSZombie::createStructure(*this, jsNull()));
206#endif
207
208    wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable);
209
210#if PLATFORM(MAC)
211    startProfilerServerIfNeeded();
212#endif
213#if ENABLE(JIT) && ENABLE(INTERPRETER)
214#if USE(CF)
215    CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman);
216    CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication);
217    if (canUseJIT) {
218        m_canUseJIT = kCFBooleanTrue == canUseJIT;
219        CFRelease(canUseJIT);
220    } else {
221      char* canUseJITString = getenv("JavaScriptCoreUseJIT");
222      m_canUseJIT = !canUseJITString || atoi(canUseJITString);
223    }
224    CFRelease(canUseJITKey);
225#elif OS(UNIX)
226    char* canUseJITString = getenv("JavaScriptCoreUseJIT");
227    m_canUseJIT = !canUseJITString || atoi(canUseJITString);
228#else
229    m_canUseJIT = true;
230#endif
231#endif
232#if ENABLE(JIT)
233#if ENABLE(INTERPRETER)
234    if (m_canUseJIT)
235        m_canUseJIT = executableAllocator.isValid();
236#endif
237    jitStubs = new JITThunks(this);
238#endif
239}
240
241void JSGlobalData::clearBuiltinStructures()
242{
243    structureStructure.clear();
244    activationStructure.clear();
245    interruptedExecutionErrorStructure.clear();
246    terminatedExecutionErrorStructure.clear();
247    staticScopeStructure.clear();
248    strictEvalActivationStructure.clear();
249    stringStructure.clear();
250    notAnObjectStructure.clear();
251    propertyNameIteratorStructure.clear();
252    getterSetterStructure.clear();
253    apiWrapperStructure.clear();
254    scopeChainNodeStructure.clear();
255    executableStructure.clear();
256    nativeExecutableStructure.clear();
257    evalExecutableStructure.clear();
258    programExecutableStructure.clear();
259    functionExecutableStructure.clear();
260    dummyMarkableCellStructure.clear();
261    structureChainStructure.clear();
262
263#if ENABLE(JSC_ZOMBIES)
264    zombieStructure.clear();
265#endif
266}
267
268JSGlobalData::~JSGlobalData()
269{
270    // By the time this is destroyed, heap.destroy() must already have been called.
271
272    delete interpreter;
273#ifndef NDEBUG
274    // Zeroing out to make the behavior more predictable when someone attempts to use a deleted instance.
275    interpreter = 0;
276#endif
277
278    arrayTable->deleteTable();
279    dateTable->deleteTable();
280    jsonTable->deleteTable();
281    mathTable->deleteTable();
282    numberTable->deleteTable();
283    objectConstructorTable->deleteTable();
284    regExpTable->deleteTable();
285    regExpConstructorTable->deleteTable();
286    stringTable->deleteTable();
287
288    fastDelete(const_cast<HashTable*>(arrayTable));
289    fastDelete(const_cast<HashTable*>(dateTable));
290    fastDelete(const_cast<HashTable*>(jsonTable));
291    fastDelete(const_cast<HashTable*>(mathTable));
292    fastDelete(const_cast<HashTable*>(numberTable));
293    fastDelete(const_cast<HashTable*>(objectConstructorTable));
294    fastDelete(const_cast<HashTable*>(regExpTable));
295    fastDelete(const_cast<HashTable*>(regExpConstructorTable));
296    fastDelete(const_cast<HashTable*>(stringTable));
297
298    delete parser;
299    delete lexer;
300
301    deleteAllValues(opaqueJSClassData);
302
303    delete emptyList;
304
305    delete propertyNames;
306    if (globalDataType != Default)
307        deleteIdentifierTable(identifierTable);
308
309    delete clientData;
310    delete m_regExpCache;
311#if ENABLE(REGEXP_TRACING)
312    delete m_rtTraceList;
313#endif
314}
315
316PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type)
317{
318    return adoptRef(new JSGlobalData(APIContextGroup, type));
319}
320
321PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type)
322{
323    return adoptRef(new JSGlobalData(Default, type));
324}
325
326PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type)
327{
328    return create(type);
329}
330
331bool JSGlobalData::sharedInstanceExists()
332{
333    return sharedInstanceInternal();
334}
335
336JSGlobalData& JSGlobalData::sharedInstance()
337{
338    JSGlobalData*& instance = sharedInstanceInternal();
339    if (!instance) {
340        instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef();
341#if ENABLE(JSC_MULTIPLE_THREADS)
342        instance->makeUsableFromMultipleThreads();
343#endif
344    }
345    return *instance;
346}
347
348JSGlobalData*& JSGlobalData::sharedInstanceInternal()
349{
350    ASSERT(JSLock::currentThreadIsHoldingLock());
351    static JSGlobalData* sharedInstance;
352    return sharedInstance;
353}
354
355#if ENABLE(JIT)
356NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function)
357{
358    return jitStubs->hostFunctionStub(this, function);
359}
360NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, ThunkGenerator generator)
361{
362    return jitStubs->hostFunctionStub(this, function, generator);
363}
364#else
365NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function)
366{
367    return NativeExecutable::create(*this, function, callHostFunctionAsConstructor);
368}
369#endif
370
371JSGlobalData::ClientData::~ClientData()
372{
373}
374
375void JSGlobalData::resetDateCache()
376{
377    cachedUTCOffset = NaN;
378    dstOffsetCache.reset();
379    cachedDateString = UString();
380    cachedDateStringValue = NaN;
381    dateInstanceCache.reset();
382}
383
384void JSGlobalData::startSampling()
385{
386    interpreter->startSampling();
387}
388
389void JSGlobalData::stopSampling()
390{
391    interpreter->stopSampling();
392}
393
394void JSGlobalData::dumpSampleData(ExecState* exec)
395{
396    interpreter->dumpSampleData(exec);
397}
398
399void JSGlobalData::recompileAllJSFunctions()
400{
401    // If JavaScript is running, it's not safe to recompile, since we'll end
402    // up throwing away code that is live on the stack.
403    ASSERT(!dynamicGlobalObject);
404
405    Recompiler recompiler;
406    heap.forEach(recompiler);
407}
408
409#if ENABLE(REGEXP_TRACING)
410void JSGlobalData::addRegExpToTrace(PassRefPtr<RegExp> regExp)
411{
412    m_rtTraceList->add(regExp);
413}
414
415void JSGlobalData::dumpRegExpTrace()
416{
417    // The first RegExp object is ignored.  It is create by the RegExpPrototype ctor and not used.
418    RTTraceList::iterator iter = ++m_rtTraceList->begin();
419
420    if (iter != m_rtTraceList->end()) {
421        printf("\nRegExp Tracing\n");
422        printf("                                                            match()    matches\n");
423        printf("Regular Expression                          JIT Address      calls      found\n");
424        printf("----------------------------------------+----------------+----------+----------\n");
425
426        unsigned reCount = 0;
427
428        for (; iter != m_rtTraceList->end(); ++iter, ++reCount)
429            (*iter)->printTraceData();
430
431        printf("%d Regular Expressions\n", reCount);
432    }
433
434    m_rtTraceList->clear();
435}
436#else
437void JSGlobalData::dumpRegExpTrace()
438{
439}
440#endif
441
442} // namespace JSC
443