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 "DebuggerActivation.h"
36 #include "FunctionConstructor.h"
37 #include "GetterSetter.h"
38 #include "Interpreter.h"
39 #include "JSActivation.h"
40 #include "JSAPIValueWrapper.h"
41 #include "JSArray.h"
42 #include "JSByteArray.h"
43 #include "JSClassRef.h"
44 #include "JSFunction.h"
45 #include "JSLock.h"
46 #include "JSNotAnObject.h"
47 #include "JSPropertyNameIterator.h"
48 #include "JSStaticScopeObject.h"
49 #include "JSZombie.h"
50 #include "Lexer.h"
51 #include "Lookup.h"
52 #include "Nodes.h"
53 #include "Parser.h"
54 #include "RegExpCache.h"
55 #include "StrictEvalActivation.h"
56 #include <wtf/WTFThreadData.h>
57 #if ENABLE(REGEXP_TRACING)
58 #include "RegExp.h"
59 #endif
60 
61 
62 #if ENABLE(JSC_MULTIPLE_THREADS)
63 #include <wtf/Threading.h>
64 #endif
65 
66 #if PLATFORM(MAC)
67 #include "ProfilerServer.h"
68 #include <CoreFoundation/CoreFoundation.h>
69 #endif
70 
71 using namespace WTF;
72 
73 namespace {
74 
75 using namespace JSC;
76 
77 class Recompiler {
78 public:
79     void operator()(JSCell*);
80 };
81 
operator ()(JSCell * cell)82 inline void Recompiler::operator()(JSCell* cell)
83 {
84     if (!cell->inherits(&JSFunction::s_info))
85         return;
86     JSFunction* function = asFunction(cell);
87     if (function->executable()->isHostFunction())
88         return;
89     function->jsExecutable()->discardCode();
90 }
91 
92 } // namespace
93 
94 namespace JSC {
95 
96 extern JSC_CONST_HASHTABLE HashTable arrayTable;
97 extern JSC_CONST_HASHTABLE HashTable jsonTable;
98 extern JSC_CONST_HASHTABLE HashTable dateTable;
99 extern JSC_CONST_HASHTABLE HashTable mathTable;
100 extern JSC_CONST_HASHTABLE HashTable numberTable;
101 extern JSC_CONST_HASHTABLE HashTable objectConstructorTable;
102 extern JSC_CONST_HASHTABLE HashTable regExpTable;
103 extern JSC_CONST_HASHTABLE HashTable regExpConstructorTable;
104 extern JSC_CONST_HASHTABLE HashTable stringTable;
105 
106 void* JSGlobalData::jsArrayVPtr;
107 void* JSGlobalData::jsByteArrayVPtr;
108 void* JSGlobalData::jsStringVPtr;
109 void* JSGlobalData::jsFunctionVPtr;
110 
111 #if COMPILER(GCC)
112 // Work around for gcc trying to coalesce our reads of the various cell vptrs
113 #define CLOBBER_MEMORY() do { \
114     asm volatile ("" : : : "memory"); \
115 } while (false)
116 #else
117 #define CLOBBER_MEMORY() do { } while (false)
118 #endif
119 
storeVPtrs()120 void JSGlobalData::storeVPtrs()
121 {
122     // Enough storage to fit a JSArray, JSByteArray, JSString, or JSFunction.
123     // COMPILE_ASSERTS below check that this is true.
124     char storage[64];
125 
126     COMPILE_ASSERT(sizeof(JSArray) <= sizeof(storage), sizeof_JSArray_must_be_less_than_storage);
127     JSCell* jsArray = new (storage) JSArray(JSArray::VPtrStealingHack);
128     CLOBBER_MEMORY();
129     JSGlobalData::jsArrayVPtr = jsArray->vptr();
130 
131     COMPILE_ASSERT(sizeof(JSByteArray) <= sizeof(storage), sizeof_JSByteArray_must_be_less_than_storage);
132     JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack);
133     CLOBBER_MEMORY();
134     JSGlobalData::jsByteArrayVPtr = jsByteArray->vptr();
135 
136     COMPILE_ASSERT(sizeof(JSString) <= sizeof(storage), sizeof_JSString_must_be_less_than_storage);
137     JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack);
138     CLOBBER_MEMORY();
139     JSGlobalData::jsStringVPtr = jsString->vptr();
140 
141     COMPILE_ASSERT(sizeof(JSFunction) <= sizeof(storage), sizeof_JSFunction_must_be_less_than_storage);
142     JSCell* jsFunction = new (storage) JSFunction(JSCell::VPtrStealingHack);
143     CLOBBER_MEMORY();
144     JSGlobalData::jsFunctionVPtr = jsFunction->vptr();
145 }
146 
JSGlobalData(GlobalDataType globalDataType,ThreadStackType threadStackType)147 JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType)
148     : globalDataType(globalDataType)
149     , clientData(0)
150     , arrayTable(fastNew<HashTable>(JSC::arrayTable))
151     , dateTable(fastNew<HashTable>(JSC::dateTable))
152     , jsonTable(fastNew<HashTable>(JSC::jsonTable))
153     , mathTable(fastNew<HashTable>(JSC::mathTable))
154     , numberTable(fastNew<HashTable>(JSC::numberTable))
155     , objectConstructorTable(fastNew<HashTable>(JSC::objectConstructorTable))
156     , regExpTable(fastNew<HashTable>(JSC::regExpTable))
157     , regExpConstructorTable(fastNew<HashTable>(JSC::regExpConstructorTable))
158     , stringTable(fastNew<HashTable>(JSC::stringTable))
159     , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable())
160     , propertyNames(new CommonIdentifiers(this))
161     , emptyList(new MarkedArgumentBuffer)
162     , lexer(new Lexer(this))
163     , parser(new Parser)
164     , interpreter(0)
165     , heap(this)
166     , globalObjectCount(0)
167     , dynamicGlobalObject(0)
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     debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, jsNull()));
186     activationStructure.set(*this, JSActivation::createStructure(*this, jsNull()));
187     interruptedExecutionErrorStructure.set(*this, JSNonFinalObject::createStructure(*this, jsNull()));
188     terminatedExecutionErrorStructure.set(*this, JSNonFinalObject::createStructure(*this, jsNull()));
189     staticScopeStructure.set(*this, JSStaticScopeObject::createStructure(*this, jsNull()));
190     strictEvalActivationStructure.set(*this, StrictEvalActivation::createStructure(*this, jsNull()));
191     stringStructure.set(*this, JSString::createStructure(*this, jsNull()));
192     notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, jsNull()));
193     propertyNameIteratorStructure.set(*this, JSPropertyNameIterator::createStructure(*this, jsNull()));
194     getterSetterStructure.set(*this, GetterSetter::createStructure(*this, jsNull()));
195     apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, jsNull()));
196     scopeChainNodeStructure.set(*this, ScopeChainNode::createStructure(*this, jsNull()));
197     executableStructure.set(*this, ExecutableBase::createStructure(*this, jsNull()));
198     nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, jsNull()));
199     evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, jsNull()));
200     programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, jsNull()));
201     functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, jsNull()));
202     dummyMarkableCellStructure.set(*this, JSCell::createDummyStructure(*this));
203     structureChainStructure.set(*this, StructureChain::createStructure(*this, jsNull()));
204 
205 #if ENABLE(JSC_ZOMBIES)
206     zombieStructure.set(*this, JSZombie::createStructure(*this, jsNull()));
207 #endif
208 
209     wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable);
210 
211 #if PLATFORM(MAC)
212     startProfilerServerIfNeeded();
213 #endif
214 #if ENABLE(JIT) && ENABLE(INTERPRETER)
215 #if USE(CF)
216     CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman);
217     CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication);
218     if (canUseJIT) {
219         m_canUseJIT = kCFBooleanTrue == canUseJIT;
220         CFRelease(canUseJIT);
221     } else {
222       char* canUseJITString = getenv("JavaScriptCoreUseJIT");
223       m_canUseJIT = !canUseJITString || atoi(canUseJITString);
224     }
225     CFRelease(canUseJITKey);
226 #elif OS(UNIX)
227     char* canUseJITString = getenv("JavaScriptCoreUseJIT");
228     m_canUseJIT = !canUseJITString || atoi(canUseJITString);
229 #else
230     m_canUseJIT = true;
231 #endif
232 #endif
233 #if ENABLE(JIT)
234 #if ENABLE(INTERPRETER)
235     if (m_canUseJIT)
236         m_canUseJIT = executableAllocator.isValid();
237 #endif
238     jitStubs = adoptPtr(new JITThunks(this));
239 #endif
240 }
241 
clearBuiltinStructures()242 void JSGlobalData::clearBuiltinStructures()
243 {
244     structureStructure.clear();
245     debuggerActivationStructure.clear();
246     activationStructure.clear();
247     interruptedExecutionErrorStructure.clear();
248     terminatedExecutionErrorStructure.clear();
249     staticScopeStructure.clear();
250     strictEvalActivationStructure.clear();
251     stringStructure.clear();
252     notAnObjectStructure.clear();
253     propertyNameIteratorStructure.clear();
254     getterSetterStructure.clear();
255     apiWrapperStructure.clear();
256     scopeChainNodeStructure.clear();
257     executableStructure.clear();
258     nativeExecutableStructure.clear();
259     evalExecutableStructure.clear();
260     programExecutableStructure.clear();
261     functionExecutableStructure.clear();
262     dummyMarkableCellStructure.clear();
263     structureChainStructure.clear();
264 
265 #if ENABLE(JSC_ZOMBIES)
266     zombieStructure.clear();
267 #endif
268 }
269 
~JSGlobalData()270 JSGlobalData::~JSGlobalData()
271 {
272     // By the time this is destroyed, heap.destroy() must already have been called.
273 
274     delete interpreter;
275 #ifndef NDEBUG
276     // Zeroing out to make the behavior more predictable when someone attempts to use a deleted instance.
277     interpreter = 0;
278 #endif
279 
280     arrayTable->deleteTable();
281     dateTable->deleteTable();
282     jsonTable->deleteTable();
283     mathTable->deleteTable();
284     numberTable->deleteTable();
285     objectConstructorTable->deleteTable();
286     regExpTable->deleteTable();
287     regExpConstructorTable->deleteTable();
288     stringTable->deleteTable();
289 
290     fastDelete(const_cast<HashTable*>(arrayTable));
291     fastDelete(const_cast<HashTable*>(dateTable));
292     fastDelete(const_cast<HashTable*>(jsonTable));
293     fastDelete(const_cast<HashTable*>(mathTable));
294     fastDelete(const_cast<HashTable*>(numberTable));
295     fastDelete(const_cast<HashTable*>(objectConstructorTable));
296     fastDelete(const_cast<HashTable*>(regExpTable));
297     fastDelete(const_cast<HashTable*>(regExpConstructorTable));
298     fastDelete(const_cast<HashTable*>(stringTable));
299 
300     delete parser;
301     delete lexer;
302 
303     deleteAllValues(opaqueJSClassData);
304 
305     delete emptyList;
306 
307     delete propertyNames;
308     if (globalDataType != Default)
309         deleteIdentifierTable(identifierTable);
310 
311     delete clientData;
312     delete m_regExpCache;
313 #if ENABLE(REGEXP_TRACING)
314     delete m_rtTraceList;
315 #endif
316 }
317 
createContextGroup(ThreadStackType type)318 PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type)
319 {
320     return adoptRef(new JSGlobalData(APIContextGroup, type));
321 }
322 
create(ThreadStackType type)323 PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type)
324 {
325     return adoptRef(new JSGlobalData(Default, type));
326 }
327 
createLeaked(ThreadStackType type)328 PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type)
329 {
330     return create(type);
331 }
332 
sharedInstanceExists()333 bool JSGlobalData::sharedInstanceExists()
334 {
335     return sharedInstanceInternal();
336 }
337 
sharedInstance()338 JSGlobalData& JSGlobalData::sharedInstance()
339 {
340     JSGlobalData*& instance = sharedInstanceInternal();
341     if (!instance) {
342         instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef();
343 #if ENABLE(JSC_MULTIPLE_THREADS)
344         instance->makeUsableFromMultipleThreads();
345 #endif
346     }
347     return *instance;
348 }
349 
sharedInstanceInternal()350 JSGlobalData*& JSGlobalData::sharedInstanceInternal()
351 {
352     ASSERT(JSLock::currentThreadIsHoldingLock());
353     static JSGlobalData* sharedInstance;
354     return sharedInstance;
355 }
356 
357 #if ENABLE(JIT)
getHostFunction(NativeFunction function)358 NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function)
359 {
360     return jitStubs->hostFunctionStub(this, function);
361 }
getHostFunction(NativeFunction function,ThunkGenerator generator)362 NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, ThunkGenerator generator)
363 {
364     return jitStubs->hostFunctionStub(this, function, generator);
365 }
366 #else
getHostFunction(NativeFunction function)367 NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function)
368 {
369     return NativeExecutable::create(*this, function, callHostFunctionAsConstructor);
370 }
371 #endif
372 
~ClientData()373 JSGlobalData::ClientData::~ClientData()
374 {
375 }
376 
resetDateCache()377 void JSGlobalData::resetDateCache()
378 {
379     localTimeOffsetCache.reset();
380     cachedDateString = UString();
381     cachedDateStringValue = NaN;
382     dateInstanceCache.reset();
383 }
384 
startSampling()385 void JSGlobalData::startSampling()
386 {
387     interpreter->startSampling();
388 }
389 
stopSampling()390 void JSGlobalData::stopSampling()
391 {
392     interpreter->stopSampling();
393 }
394 
dumpSampleData(ExecState * exec)395 void JSGlobalData::dumpSampleData(ExecState* exec)
396 {
397     interpreter->dumpSampleData(exec);
398 }
399 
recompileAllJSFunctions()400 void JSGlobalData::recompileAllJSFunctions()
401 {
402     // If JavaScript is running, it's not safe to recompile, since we'll end
403     // up throwing away code that is live on the stack.
404     ASSERT(!dynamicGlobalObject);
405 
406     Recompiler recompiler;
407     heap.forEach(recompiler);
408 }
409 
410 #if ENABLE(REGEXP_TRACING)
addRegExpToTrace(PassRefPtr<RegExp> regExp)411 void JSGlobalData::addRegExpToTrace(PassRefPtr<RegExp> regExp)
412 {
413     m_rtTraceList->add(regExp);
414 }
415 
dumpRegExpTrace()416 void JSGlobalData::dumpRegExpTrace()
417 {
418     // The first RegExp object is ignored.  It is create by the RegExpPrototype ctor and not used.
419     RTTraceList::iterator iter = ++m_rtTraceList->begin();
420 
421     if (iter != m_rtTraceList->end()) {
422         printf("\nRegExp Tracing\n");
423         printf("                                                            match()    matches\n");
424         printf("Regular Expression                          JIT Address      calls      found\n");
425         printf("----------------------------------------+----------------+----------+----------\n");
426 
427         unsigned reCount = 0;
428 
429         for (; iter != m_rtTraceList->end(); ++iter, ++reCount)
430             (*iter)->printTraceData();
431 
432         printf("%d Regular Expressions\n", reCount);
433     }
434 
435     m_rtTraceList->clear();
436 }
437 #else
dumpRegExpTrace()438 void JSGlobalData::dumpRegExpTrace()
439 {
440 }
441 #endif
442 
443 } // namespace JSC
444