blob: c4be35ee38c38f1fe3b7620a77dcfd5ea6b59d34 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2007-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010030#define USE_NEW_QUERY_CALLBACKS
31
Steve Blocka7e24c12009-10-30 11:49:00 +000032#include "v8.h"
33
34#include "heap.h"
35#include "cctest.h"
36
37using namespace v8;
38
39
40enum Expectations {
41 EXPECT_RESULT,
42 EXPECT_EXCEPTION
43};
44
45
46// A DeclarationContext holds a reference to a v8::Context and keeps
47// track of various declaration related counters to make it easier to
48// track if global declarations in the presence of interceptors behave
49// the right way.
50class DeclarationContext {
51 public:
52 DeclarationContext();
53
54 virtual ~DeclarationContext() {
55 if (is_initialized_) {
56 context_->Exit();
57 context_.Dispose();
58 }
59 }
60
61 void Check(const char* source,
62 int get, int set, int has,
63 Expectations expectations,
64 v8::Handle<Value> value = Local<Value>());
65
66 int get_count() const { return get_count_; }
67 int set_count() const { return set_count_; }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010068 int query_count() const { return query_count_; }
Steve Blocka7e24c12009-10-30 11:49:00 +000069
70 protected:
71 virtual v8::Handle<Value> Get(Local<String> key);
72 virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010073 virtual v8::Handle<Integer> Query(Local<String> key);
Steve Blocka7e24c12009-10-30 11:49:00 +000074
75 void InitializeIfNeeded();
76
77 // Get the holder for the interceptor. Default to the instance template
78 // but may be overwritten.
79 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
80 return function->InstanceTemplate();
81 }
82
83 // The handlers are called as static functions that forward
84 // to the instance specific virtual methods.
85 static v8::Handle<Value> HandleGet(Local<String> key,
86 const AccessorInfo& info);
87 static v8::Handle<Value> HandleSet(Local<String> key,
88 Local<Value> value,
89 const AccessorInfo& info);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010090 static v8::Handle<Integer> HandleQuery(Local<String> key,
91 const AccessorInfo& info);
Steve Blocka7e24c12009-10-30 11:49:00 +000092
93 private:
94 bool is_initialized_;
95 Persistent<Context> context_;
96 Local<String> property_;
97
98 int get_count_;
99 int set_count_;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100100 int query_count_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000101
102 static DeclarationContext* GetInstance(const AccessorInfo& info);
103};
104
105
106DeclarationContext::DeclarationContext()
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100107 : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000108 // Do nothing.
109}
110
111
112void DeclarationContext::InitializeIfNeeded() {
113 if (is_initialized_) return;
114 HandleScope scope;
115 Local<FunctionTemplate> function = FunctionTemplate::New();
116 Local<Value> data = External::New(this);
117 GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
118 &HandleSet,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100119 &HandleQuery,
Steve Blocka7e24c12009-10-30 11:49:00 +0000120 0, 0,
121 data);
122 context_ = Context::New(0, function->InstanceTemplate(), Local<Value>());
123 context_->Enter();
124 is_initialized_ = true;
125}
126
127
128void DeclarationContext::Check(const char* source,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100129 int get, int set, int query,
Steve Blocka7e24c12009-10-30 11:49:00 +0000130 Expectations expectations,
131 v8::Handle<Value> value) {
132 InitializeIfNeeded();
133 // A retry after a GC may pollute the counts, so perform gc now
134 // to avoid that.
135 v8::internal::Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
136 HandleScope scope;
137 TryCatch catcher;
138 catcher.SetVerbose(true);
139 Local<Value> result = Script::Compile(String::New(source))->Run();
140 CHECK_EQ(get, get_count());
141 CHECK_EQ(set, set_count());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100142 CHECK_EQ(query, query_count());
Steve Blocka7e24c12009-10-30 11:49:00 +0000143 if (expectations == EXPECT_RESULT) {
144 CHECK(!catcher.HasCaught());
145 if (!value.IsEmpty()) {
146 CHECK_EQ(value, result);
147 }
148 } else {
149 CHECK(expectations == EXPECT_EXCEPTION);
150 CHECK(catcher.HasCaught());
151 if (!value.IsEmpty()) {
152 CHECK_EQ(value, catcher.Exception());
153 }
154 }
155}
156
157
158v8::Handle<Value> DeclarationContext::HandleGet(Local<String> key,
159 const AccessorInfo& info) {
160 DeclarationContext* context = GetInstance(info);
161 context->get_count_++;
162 return context->Get(key);
163}
164
165
166v8::Handle<Value> DeclarationContext::HandleSet(Local<String> key,
167 Local<Value> value,
168 const AccessorInfo& info) {
169 DeclarationContext* context = GetInstance(info);
170 context->set_count_++;
171 return context->Set(key, value);
172}
173
174
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100175v8::Handle<Integer> DeclarationContext::HandleQuery(Local<String> key,
176 const AccessorInfo& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000177 DeclarationContext* context = GetInstance(info);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100178 context->query_count_++;
179 return context->Query(key);
Steve Blocka7e24c12009-10-30 11:49:00 +0000180}
181
182
183DeclarationContext* DeclarationContext::GetInstance(const AccessorInfo& info) {
184 return static_cast<DeclarationContext*>(External::Unwrap(info.Data()));
185}
186
187
188v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
189 return v8::Handle<Value>();
190}
191
192
193v8::Handle<Value> DeclarationContext::Set(Local<String> key,
194 Local<Value> value) {
195 return v8::Handle<Value>();
196}
197
198
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100199v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
200 return v8::Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000201}
202
203
204// Test global declaration of a property the interceptor doesn't know
205// about and doesn't handle.
206TEST(Unknown) {
207 HandleScope scope;
208
209 { DeclarationContext context;
210 context.Check("var x; x",
211 1, // access
212 1, // declaration
213 2, // declaration + initialization
214 EXPECT_RESULT, Undefined());
215 }
216
217 { DeclarationContext context;
218 context.Check("var x = 0; x",
219 1, // access
220 2, // declaration + initialization
221 2, // declaration + initialization
222 EXPECT_RESULT, Number::New(0));
223 }
224
225 { DeclarationContext context;
226 context.Check("function x() { }; x",
227 1, // access
228 1, // declaration
229 0,
230 EXPECT_RESULT);
231 }
232
233 { DeclarationContext context;
234 context.Check("const x; x",
235 1, // access
236 2, // declaration + initialization
237 2, // declaration + initialization
238 EXPECT_RESULT, Undefined());
239 }
240
241 { DeclarationContext context;
242 context.Check("const x = 0; x",
243 1, // access
244 2, // declaration + initialization
245 2, // declaration + initialization
246 EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
247 }
248}
249
250
251
252class PresentPropertyContext: public DeclarationContext {
253 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100254 virtual v8::Handle<Integer> Query(Local<String> key) {
255 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000256 }
257};
258
259
260
261TEST(Present) {
262 HandleScope scope;
263
264 { PresentPropertyContext context;
265 context.Check("var x; x",
266 1, // access
267 0,
268 2, // declaration + initialization
269 EXPECT_EXCEPTION); // x is not defined!
270 }
271
272 { PresentPropertyContext context;
273 context.Check("var x = 0; x",
274 1, // access
275 1, // initialization
276 2, // declaration + initialization
277 EXPECT_RESULT, Number::New(0));
278 }
279
280 { PresentPropertyContext context;
281 context.Check("function x() { }; x",
282 1, // access
283 1, // declaration
284 0,
285 EXPECT_RESULT);
286 }
287
288 { PresentPropertyContext context;
289 context.Check("const x; x",
290 0,
291 0,
292 1, // (re-)declaration
293 EXPECT_EXCEPTION); // x has already been declared!
294 }
295
296 { PresentPropertyContext context;
297 context.Check("const x = 0; x",
298 0,
299 0,
300 1, // (re-)declaration
301 EXPECT_EXCEPTION); // x has already been declared!
302 }
303}
304
305
306
307class AbsentPropertyContext: public DeclarationContext {
308 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100309 virtual v8::Handle<Integer> Query(Local<String> key) {
310 return v8::Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000311 }
312};
313
314
315TEST(Absent) {
316 HandleScope scope;
317
318 { AbsentPropertyContext context;
319 context.Check("var x; x",
320 1, // access
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100321 1, // declaration
Steve Blocka7e24c12009-10-30 11:49:00 +0000322 2, // declaration + initialization
323 EXPECT_RESULT, Undefined());
324 }
325
326 { AbsentPropertyContext context;
327 context.Check("var x = 0; x",
328 1, // access
329 2, // declaration + initialization
330 2, // declaration + initialization
331 EXPECT_RESULT, Number::New(0));
332 }
333
334 { AbsentPropertyContext context;
335 context.Check("function x() { }; x",
336 1, // access
337 1, // declaration
338 0,
339 EXPECT_RESULT);
340 }
341
342 { AbsentPropertyContext context;
343 context.Check("const x; x",
344 1, // access
345 2, // declaration + initialization
346 2, // declaration + initializetion
347 EXPECT_RESULT, Undefined());
348 }
349
350 { AbsentPropertyContext context;
351 context.Check("const x = 0; x",
352 1, // access
353 2, // declaration + initialization
354 2, // declaration + initialization
355 EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
356 }
357
358 { AbsentPropertyContext context;
359 context.Check("if (false) { var x = 0 }; x",
360 1, // access
361 1, // declaration
362 1, // declaration + initialization
363 EXPECT_RESULT, Undefined());
364 }
365}
366
367
368
369class AppearingPropertyContext: public DeclarationContext {
370 public:
371 enum State {
372 DECLARE,
373 INITIALIZE_IF_ASSIGN,
374 UNKNOWN
375 };
376
377 AppearingPropertyContext() : state_(DECLARE) { }
378
379 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100380 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000381 switch (state_) {
382 case DECLARE:
383 // Force declaration by returning that the
384 // property is absent.
385 state_ = INITIALIZE_IF_ASSIGN;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100386 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000387 case INITIALIZE_IF_ASSIGN:
388 // Return that the property is present so we only get the
389 // setter called when initializing with a value.
390 state_ = UNKNOWN;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100391 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000392 default:
393 CHECK(state_ == UNKNOWN);
394 break;
395 }
396 // Do the lookup in the object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100397 return v8::Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000398 }
399
400 private:
401 State state_;
402};
403
404
405TEST(Appearing) {
406 HandleScope scope;
407
408 { AppearingPropertyContext context;
409 context.Check("var x; x",
410 1, // access
411 1, // declaration
412 2, // declaration + initialization
413 EXPECT_RESULT, Undefined());
414 }
415
416 { AppearingPropertyContext context;
417 context.Check("var x = 0; x",
418 1, // access
419 2, // declaration + initialization
420 2, // declaration + initialization
421 EXPECT_RESULT, Number::New(0));
422 }
423
424 { AppearingPropertyContext context;
425 context.Check("function x() { }; x",
426 1, // access
427 1, // declaration
428 0,
429 EXPECT_RESULT);
430 }
431
432 { AppearingPropertyContext context;
433 context.Check("const x; x",
434 0,
435 1, // declaration
436 2, // declaration + initialization
437 EXPECT_EXCEPTION); // x has already been declared!
438 }
439
440 { AppearingPropertyContext context;
441 context.Check("const x = 0; x",
442 0,
443 1, // declaration
444 2, // declaration + initialization
445 EXPECT_EXCEPTION); // x has already been declared!
446 }
447}
448
449
450
451class ReappearingPropertyContext: public DeclarationContext {
452 public:
453 enum State {
454 DECLARE,
455 DONT_DECLARE,
456 INITIALIZE,
457 UNKNOWN
458 };
459
460 ReappearingPropertyContext() : state_(DECLARE) { }
461
462 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100463 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000464 switch (state_) {
465 case DECLARE:
466 // Force the first declaration by returning that
467 // the property is absent.
468 state_ = DONT_DECLARE;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100469 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000470 case DONT_DECLARE:
471 // Ignore the second declaration by returning
472 // that the property is already there.
473 state_ = INITIALIZE;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100474 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000475 case INITIALIZE:
476 // Force an initialization by returning that
477 // the property is absent. This will make sure
478 // that the setter is called and it will not
479 // lead to redeclaration conflicts (yet).
480 state_ = UNKNOWN;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100481 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000482 default:
483 CHECK(state_ == UNKNOWN);
484 break;
485 }
486 // Do the lookup in the object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100487 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000488 }
489
490 private:
491 State state_;
492};
493
494
495TEST(Reappearing) {
496 HandleScope scope;
497
498 { ReappearingPropertyContext context;
499 context.Check("const x; var x = 0",
500 0,
501 2, // var declaration + const initialization
502 4, // 2 x declaration + 2 x initialization
503 EXPECT_EXCEPTION); // x has already been declared!
504 }
505}
506
507
508
509class ExistsInPrototypeContext: public DeclarationContext {
510 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100511 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000512 // Let it seem that the property exists in the prototype object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100513 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000514 }
515
516 // Use the prototype as the holder for the interceptors.
517 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
518 return function->PrototypeTemplate();
519 }
520};
521
522
523TEST(ExistsInPrototype) {
524 HandleScope scope;
525
526 // Sanity check to make sure that the holder of the interceptor
527 // really is the prototype object.
528 { ExistsInPrototypeContext context;
529 context.Check("this.x = 87; this.x",
530 0,
531 0,
532 0,
533 EXPECT_RESULT, Number::New(87));
534 }
535
536 { ExistsInPrototypeContext context;
537 context.Check("var x; x",
538 1, // get
539 0,
540 1, // declaration
541 EXPECT_EXCEPTION);
542 }
543
544 { ExistsInPrototypeContext context;
545 context.Check("var x = 0; x",
546 0,
547 0,
548 1, // declaration
549 EXPECT_RESULT, Number::New(0));
550 }
551
552 { ExistsInPrototypeContext context;
553 context.Check("const x; x",
554 0,
555 0,
556 1, // declaration
557 EXPECT_RESULT, Undefined());
558 }
559
560 { ExistsInPrototypeContext context;
561 context.Check("const x = 0; x",
562 0,
563 0,
564 1, // declaration
565 EXPECT_RESULT, Number::New(0));
566 }
567}
568
569
570
571class AbsentInPrototypeContext: public DeclarationContext {
572 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100573 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000574 // Let it seem that the property is absent in the prototype object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100575 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000576 }
577
578 // Use the prototype as the holder for the interceptors.
579 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
580 return function->PrototypeTemplate();
581 }
582};
583
584
585TEST(AbsentInPrototype) {
586 HandleScope scope;
587
588 { AbsentInPrototypeContext context;
589 context.Check("if (false) { var x = 0; }; x",
590 0,
591 0,
592 1, // declaration
593 EXPECT_RESULT, Undefined());
594 }
595}