source: webkit/trunk/JavaScriptCore/kjs/testkjs.cpp@ 19894

Last change on this file since 19894 was 19894, checked in by ggaren, 18 years ago

JavaScriptCore:

Reviewed by Maciej Stachowiak.


Fixed http://bugs.webkit.org/show_bug.cgi?id=12659 | <rdar://problem/4954306>
JS objects not collected after closing window @ ebay.com/maps.google.com

Don't GC in the Interpreter destructor. For that to work, the Interpreter
would have to NULL out all of its ProtectedPtrs before calling collect(). But
we've decided that we don't want things to work that way, anyway. We want the
client to be in charge of manual GC so that it can optimize cases when
it will be destroying many interpreters at once
(e.g., http://bugs.webkit.org/show_bug.cgi?id=12900).


Also removed Interpreter::collect() because it was redundant with
Collector::collect().

  • JavaScriptCore.exp:
  • kjs/interpreter.cpp: (KJS::Interpreter::~Interpreter):
  • kjs/testkjs.cpp: (TestFunctionImp::callAsFunction):

LayoutTests:

Reviewed by Maciej Stachowiak.


Test for http://bugs.webkit.org/show_bug.cgi?id=12659 | <rdar://problem/4954306>
JS objects not collected after closing window @ ebay.com/maps.google.com


  • fast/leaks/003-expected.txt: Added.
  • fast/leaks/003.html: Added.
  • fast/dom/gc-10.html: Bumped the wiggle room threshold on this test by 2 because it's failing on my machine with the patch applied but, according to the test notes, we're still well within the margin of error.

WebCore:

Reviewed by Maciej Stachowiak.


Fixed http://bugs.webkit.org/show_bug.cgi?id=12659 | <rdar://problem/4954306>
JS objects not collected after closing window @ ebay.com/maps.google.com

Garbage collect in the KJSProxy destructor, after clearing our reference
to the interpreter, because that's when the interpreter has torn down fully.

(Technically speaking, we can't *prove* that we have the only reference to
our interpreter, but that's how it works in practice, and manual garbage
collection is just an opportunistic optimization, so it's OK for it to
work in practice even if it can't be proven in theory.)


Layout tests pass. No leaks reported.

  • bindings/js/kjs_proxy.cpp: (WebCore::KJSProxy::~KJSProxy):
  • bindings/js/kjs_proxy.h:
  • page/Page.cpp: (WebCore::Page::~Page): Merged pageDestroyed() calls. Moved debug-only code to the bottom.
  • page/Frame.cpp: (WebCore::Frame::~Frame): Don't call getObject() because globalObject() returns a JSObject* already, and the call can leave a pointer to the Window object on the stack. Don't check for NULL because it is an invariant of JavaScriptCore that no JSObject* can be NULL. Do use a volatile pointer for w because the 'w = 0' assignment just screams to the compiler, "Don't generate any code for me!"
  • Property svn:eol-style set to native
File size: 9.0 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2000 Harri Porten ([email protected])
5 * Copyright (C) 2004-2006 Apple Computer, Inc.
6 * Copyright (C) 2006 Björn Graf ([email protected])
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "collector.h"
27
28#include <wtf/HashTraits.h>
29#include "JSLock.h"
30#include "object.h"
31#include "Parser.h"
32
33#include <math.h>
34#include <stdio.h>
35
36#include <string.h>
37#if HAVE(SYS_TIME_H)
38#include <sys/time.h>
39#endif
40
41#include "protect.h"
42
43#if PLATFORM(WIN_OS)
44#include <windows.h>
45#include <crtdbg.h>
46#endif
47
48using namespace KJS;
49using namespace WTF;
50
51static void testIsInteger();
52static char* createStringWithContentsOfFile(const char* fileName);
53
54class StopWatch
55{
56public:
57 void start();
58 void stop();
59 long getElapsedMS(); // call stop() first
60
61private:
62#if PLATFORM(WIN_OS)
63 DWORD m_startTime;
64 DWORD m_stopTime;
65#else
66 // Windows does not have timeval, disabling this class for now (bug 7399)
67 timeval m_startTime;
68 timeval m_stopTime;
69#endif
70};
71
72void StopWatch::start()
73{
74#if PLATFORM(WIN_OS)
75 m_startTime = timeGetTime();
76#else
77 gettimeofday(&m_startTime, 0);
78#endif
79}
80
81void StopWatch::stop()
82{
83#if PLATFORM(WIN_OS)
84 m_stopTime = timeGetTime();
85#else
86 gettimeofday(&m_stopTime, 0);
87#endif
88}
89
90long StopWatch::getElapsedMS()
91{
92#if PLATFORM(WIN_OS)
93 return m_stopTime - m_startTime;
94#else
95 timeval elapsedTime;
96 timersub(&m_stopTime, &m_startTime, &elapsedTime);
97
98 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0);
99#endif
100}
101
102class GlobalImp : public JSObject {
103public:
104 virtual UString className() const { return "global"; }
105};
106
107class TestFunctionImp : public JSObject {
108public:
109 TestFunctionImp(int i, int length);
110 virtual bool implementsCall() const { return true; }
111 virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List &args);
112
113 enum { Print, Debug, Quit, GC, Version, Run };
114
115private:
116 int id;
117};
118
119TestFunctionImp::TestFunctionImp(int i, int length) : JSObject(), id(i)
120{
121 putDirect(lengthPropertyName,length,DontDelete|ReadOnly|DontEnum);
122}
123
124JSValue* TestFunctionImp::callAsFunction(ExecState* exec, JSObject*, const List &args)
125{
126 switch (id) {
127 case Print:
128 printf("--> %s\n", args[0]->toString(exec).UTF8String().c_str());
129 return jsUndefined();
130 case Debug:
131 fprintf(stderr, "--> %s\n", args[0]->toString(exec).UTF8String().c_str());
132 return jsUndefined();
133 case GC:
134 {
135 JSLock lock;
136 Collector::collect();
137 return jsUndefined();
138 }
139 case Version:
140 // We need this function for compatibility with the Mozilla JS tests but for now
141 // we don't actually do any version-specific handling
142 return jsUndefined();
143 case Run:
144 {
145 StopWatch stopWatch;
146 char* fileName = strdup(args[0]->toString(exec).UTF8String().c_str());
147 char* script = createStringWithContentsOfFile(fileName);
148 if (!script)
149 return throwError(exec, GeneralError, "Could not open file.");
150
151 stopWatch.start();
152 exec->dynamicInterpreter()->evaluate(fileName, 0, script);
153 stopWatch.stop();
154
155 free(script);
156 free(fileName);
157
158 return jsNumber(stopWatch.getElapsedMS());
159 }
160 case Quit:
161 exit(0);
162 default:
163 abort();
164 }
165 return 0;
166}
167
168#if PLATFORM(WIN_OS)
169
170// Use SEH for Release builds only to get rid of the crash report dialog
171// (luckyly the same tests fail in Release and Debug builds so far). Need to
172// be in a separate main function because the kjsmain function requires object
173// unwinding.
174
175#if defined(_DEBUG)
176#define TRY
177#define EXCEPT(x)
178#else
179#define TRY __try {
180#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
181#endif
182
183#else
184
185#define TRY
186#define EXCEPT(x)
187
188#endif
189
190int kjsmain(int argc, char** argv);
191
192int main(int argc, char** argv)
193{
194#if defined(_DEBUG) && PLATFORM(WIN_OS)
195 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
196 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
197 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
198 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
199 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
200 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
201#endif
202
203 int res = 0;
204 TRY
205 res = kjsmain(argc, argv);
206 EXCEPT(res = 3)
207 return res;
208}
209
210
211bool doIt(int argc, char** argv)
212{
213 bool success = true;
214 bool prettyPrint = false;
215 GlobalImp* global = new GlobalImp();
216
217 // create interpreter
218 RefPtr<Interpreter> interp = new Interpreter(global);
219 // add debug() function
220 global->put(interp->globalExec(), "debug", new TestFunctionImp(TestFunctionImp::Debug, 1));
221 // add "print" for compatibility with the mozilla js shell
222 global->put(interp->globalExec(), "print", new TestFunctionImp(TestFunctionImp::Print, 1));
223 // add "quit" for compatibility with the mozilla js shell
224 global->put(interp->globalExec(), "quit", new TestFunctionImp(TestFunctionImp::Quit, 0));
225 // add "gc" for compatibility with the mozilla js shell
226 global->put(interp->globalExec(), "gc", new TestFunctionImp(TestFunctionImp::GC, 0));
227 // add "version" for compatibility with the mozilla js shell
228 global->put(interp->globalExec(), "version", new TestFunctionImp(TestFunctionImp::Version, 1));
229 global->put(interp->globalExec(), "run", new TestFunctionImp(TestFunctionImp::Run, 1));
230
231 Interpreter::setShouldPrintExceptions(true);
232
233 for (int i = 1; i < argc; i++) {
234 const char* fileName = argv[i];
235 if (strcmp(fileName, "-f") == 0) // mozilla test driver script uses "-f" prefix for files
236 continue;
237 if (strcmp(fileName, "-p") == 0) {
238 prettyPrint = true;
239 continue;
240 }
241
242 char* script = createStringWithContentsOfFile(fileName);
243 if (!script) {
244 success = false;
245 break; // fail early so we can catch missing files
246 }
247
248 if (prettyPrint) {
249 int errLine = 0;
250 UString errMsg;
251 UString s = Parser::prettyPrint(script, &errLine, &errMsg);
252 if (s.isNull()) {
253 fprintf(stderr, "%s:%d: %s.\n", fileName, errLine, errMsg.UTF8String().c_str());
254 success = false;
255 break;
256 }
257
258 printf("%s\n", s.UTF8String().c_str());
259
260 } else {
261 Completion completion = interp->evaluate(fileName, 0, script);
262 success = success && completion.complType() != Throw;
263 }
264
265 free(script);
266 }
267
268 return success;
269}
270
271
272int kjsmain(int argc, char** argv)
273{
274 if (argc < 2) {
275 fprintf(stderr, "Usage: testkjs file1 [file2...]\n");
276 return -1;
277 }
278
279 testIsInteger();
280
281 JSLock lock;
282
283 bool success = doIt(argc, argv);
284
285#ifndef NDEBUG
286 Collector::collect();
287#endif
288
289 if (success)
290 fprintf(stderr, "OK.\n");
291
292#ifdef KJS_DEBUG_MEM
293 Interpreter::finalCheck();
294#endif
295 return success ? 0 : 3;
296}
297
298static void testIsInteger()
299{
300 // Unit tests for WTF::IsInteger. Don't have a better place for them now.
301 // FIXME: move these once we create a unit test directory for WTF.
302
303 assert(IsInteger<bool>::value);
304 assert(IsInteger<char>::value);
305 assert(IsInteger<signed char>::value);
306 assert(IsInteger<unsigned char>::value);
307 assert(IsInteger<short>::value);
308 assert(IsInteger<unsigned short>::value);
309 assert(IsInteger<int>::value);
310 assert(IsInteger<unsigned int>::value);
311 assert(IsInteger<long>::value);
312 assert(IsInteger<unsigned long>::value);
313 assert(IsInteger<long long>::value);
314 assert(IsInteger<unsigned long long>::value);
315
316 assert(!IsInteger<char*>::value);
317 assert(!IsInteger<const char* >::value);
318 assert(!IsInteger<volatile char* >::value);
319 assert(!IsInteger<double>::value);
320 assert(!IsInteger<float>::value);
321 assert(!IsInteger<GlobalImp>::value);
322}
323
324static char* createStringWithContentsOfFile(const char* fileName)
325{
326 char* buffer;
327
328 size_t buffer_size = 0;
329 size_t buffer_capacity = 1024;
330 buffer = (char*)malloc(buffer_capacity);
331
332 FILE* f = fopen(fileName, "r");
333 if (!f) {
334 fprintf(stderr, "Could not open file: %s\n", fileName);
335 return 0;
336 }
337
338 while (!feof(f) && !ferror(f)) {
339 buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
340 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
341 buffer_capacity *= 2;
342 buffer = (char*)realloc(buffer, buffer_capacity);
343 assert(buffer);
344 }
345
346 assert(buffer_size < buffer_capacity);
347 }
348 fclose(f);
349 buffer[buffer_size] = '\0';
350
351 return buffer;
352}
Note: See TracBrowser for help on using the repository browser.