blob: 6ea4c849ee71fd5439983b12cb99575abcaebc7a [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
30#include "v8.h"
31
32#include "heap.h"
33#include "cctest.h"
34
35using namespace v8;
36
37
38enum Expectations {
39 EXPECT_RESULT,
40 EXPECT_EXCEPTION
41};
42
43
44// A DeclarationContext holds a reference to a v8::Context and keeps
45// track of various declaration related counters to make it easier to
46// track if global declarations in the presence of interceptors behave
47// the right way.
48class DeclarationContext {
49 public:
50 DeclarationContext();
51
52 virtual ~DeclarationContext() {
53 if (is_initialized_) {
54 context_->Exit();
55 context_.Dispose();
56 }
57 }
58
59 void Check(const char* source,
60 int get, int set, int has,
61 Expectations expectations,
62 v8::Handle<Value> value = Local<Value>());
63
64 int get_count() const { return get_count_; }
65 int set_count() const { return set_count_; }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010066 int query_count() const { return query_count_; }
Steve Blocka7e24c12009-10-30 11:49:00 +000067
68 protected:
69 virtual v8::Handle<Value> Get(Local<String> key);
70 virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010071 virtual v8::Handle<Integer> Query(Local<String> key);
Steve Blocka7e24c12009-10-30 11:49:00 +000072
73 void InitializeIfNeeded();
74
75 // Get the holder for the interceptor. Default to the instance template
76 // but may be overwritten.
77 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
78 return function->InstanceTemplate();
79 }
80
81 // The handlers are called as static functions that forward
82 // to the instance specific virtual methods.
83 static v8::Handle<Value> HandleGet(Local<String> key,
84 const AccessorInfo& info);
85 static v8::Handle<Value> HandleSet(Local<String> key,
86 Local<Value> value,
87 const AccessorInfo& info);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010088 static v8::Handle<Integer> HandleQuery(Local<String> key,
89 const AccessorInfo& info);
Steve Blocka7e24c12009-10-30 11:49:00 +000090
91 private:
92 bool is_initialized_;
93 Persistent<Context> context_;
94 Local<String> property_;
95
96 int get_count_;
97 int set_count_;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010098 int query_count_;
Steve Blocka7e24c12009-10-30 11:49:00 +000099
100 static DeclarationContext* GetInstance(const AccessorInfo& info);
101};
102
103
104DeclarationContext::DeclarationContext()
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100105 : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000106 // Do nothing.
107}
108
109
110void DeclarationContext::InitializeIfNeeded() {
111 if (is_initialized_) return;
112 HandleScope scope;
113 Local<FunctionTemplate> function = FunctionTemplate::New();
114 Local<Value> data = External::New(this);
115 GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
116 &HandleSet,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100117 &HandleQuery,
Steve Blocka7e24c12009-10-30 11:49:00 +0000118 0, 0,
119 data);
120 context_ = Context::New(0, function->InstanceTemplate(), Local<Value>());
121 context_->Enter();
122 is_initialized_ = true;
123}
124
125
126void DeclarationContext::Check(const char* source,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100127 int get, int set, int query,
Steve Blocka7e24c12009-10-30 11:49:00 +0000128 Expectations expectations,
129 v8::Handle<Value> value) {
130 InitializeIfNeeded();
131 // A retry after a GC may pollute the counts, so perform gc now
132 // to avoid that.
Ben Murdochf87a2032010-10-22 12:50:53 +0100133 v8::internal::Heap::CollectGarbage(v8::internal::NEW_SPACE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000134 HandleScope scope;
135 TryCatch catcher;
136 catcher.SetVerbose(true);
137 Local<Value> result = Script::Compile(String::New(source))->Run();
138 CHECK_EQ(get, get_count());
139 CHECK_EQ(set, set_count());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100140 CHECK_EQ(query, query_count());
Steve Blocka7e24c12009-10-30 11:49:00 +0000141 if (expectations == EXPECT_RESULT) {
142 CHECK(!catcher.HasCaught());
143 if (!value.IsEmpty()) {
144 CHECK_EQ(value, result);
145 }
146 } else {
147 CHECK(expectations == EXPECT_EXCEPTION);
148 CHECK(catcher.HasCaught());
149 if (!value.IsEmpty()) {
150 CHECK_EQ(value, catcher.Exception());
151 }
152 }
153}
154
155
156v8::Handle<Value> DeclarationContext::HandleGet(Local<String> key,
157 const AccessorInfo& info) {
158 DeclarationContext* context = GetInstance(info);
159 context->get_count_++;
160 return context->Get(key);
161}
162
163
164v8::Handle<Value> DeclarationContext::HandleSet(Local<String> key,
165 Local<Value> value,
166 const AccessorInfo& info) {
167 DeclarationContext* context = GetInstance(info);
168 context->set_count_++;
169 return context->Set(key, value);
170}
171
172
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100173v8::Handle<Integer> DeclarationContext::HandleQuery(Local<String> key,
174 const AccessorInfo& info) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000175 DeclarationContext* context = GetInstance(info);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100176 context->query_count_++;
177 return context->Query(key);
Steve Blocka7e24c12009-10-30 11:49:00 +0000178}
179
180
181DeclarationContext* DeclarationContext::GetInstance(const AccessorInfo& info) {
182 return static_cast<DeclarationContext*>(External::Unwrap(info.Data()));
183}
184
185
186v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
187 return v8::Handle<Value>();
188}
189
190
191v8::Handle<Value> DeclarationContext::Set(Local<String> key,
192 Local<Value> value) {
193 return v8::Handle<Value>();
194}
195
196
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100197v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
198 return v8::Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000199}
200
201
202// Test global declaration of a property the interceptor doesn't know
203// about and doesn't handle.
204TEST(Unknown) {
205 HandleScope scope;
206
207 { DeclarationContext context;
208 context.Check("var x; x",
209 1, // access
210 1, // declaration
211 2, // declaration + initialization
212 EXPECT_RESULT, Undefined());
213 }
214
215 { DeclarationContext context;
216 context.Check("var x = 0; x",
217 1, // access
218 2, // declaration + initialization
219 2, // declaration + initialization
220 EXPECT_RESULT, Number::New(0));
221 }
222
223 { DeclarationContext context;
224 context.Check("function x() { }; x",
225 1, // access
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100226 0,
Steve Blocka7e24c12009-10-30 11:49:00 +0000227 0,
228 EXPECT_RESULT);
229 }
230
231 { DeclarationContext context;
232 context.Check("const x; x",
233 1, // access
234 2, // declaration + initialization
235 2, // declaration + initialization
236 EXPECT_RESULT, Undefined());
237 }
238
239 { DeclarationContext context;
240 context.Check("const x = 0; x",
241 1, // access
242 2, // declaration + initialization
243 2, // declaration + initialization
244 EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
245 }
246}
247
248
249
250class PresentPropertyContext: public DeclarationContext {
251 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100252 virtual v8::Handle<Integer> Query(Local<String> key) {
253 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000254 }
255};
256
257
258
259TEST(Present) {
260 HandleScope scope;
261
262 { PresentPropertyContext context;
263 context.Check("var x; x",
264 1, // access
265 0,
266 2, // declaration + initialization
267 EXPECT_EXCEPTION); // x is not defined!
268 }
269
270 { PresentPropertyContext context;
271 context.Check("var x = 0; x",
272 1, // access
273 1, // initialization
274 2, // declaration + initialization
275 EXPECT_RESULT, Number::New(0));
276 }
277
278 { PresentPropertyContext context;
279 context.Check("function x() { }; x",
280 1, // access
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100281 0,
Steve Blocka7e24c12009-10-30 11:49:00 +0000282 0,
283 EXPECT_RESULT);
284 }
285
286 { PresentPropertyContext context;
287 context.Check("const x; x",
288 0,
289 0,
290 1, // (re-)declaration
291 EXPECT_EXCEPTION); // x has already been declared!
292 }
293
294 { PresentPropertyContext context;
295 context.Check("const x = 0; x",
296 0,
297 0,
298 1, // (re-)declaration
299 EXPECT_EXCEPTION); // x has already been declared!
300 }
301}
302
303
304
305class AbsentPropertyContext: public DeclarationContext {
306 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100307 virtual v8::Handle<Integer> Query(Local<String> key) {
308 return v8::Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000309 }
310};
311
312
313TEST(Absent) {
314 HandleScope scope;
315
316 { AbsentPropertyContext context;
317 context.Check("var x; x",
318 1, // access
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100319 1, // declaration
Steve Blocka7e24c12009-10-30 11:49:00 +0000320 2, // declaration + initialization
321 EXPECT_RESULT, Undefined());
322 }
323
324 { AbsentPropertyContext context;
325 context.Check("var x = 0; x",
326 1, // access
327 2, // declaration + initialization
328 2, // declaration + initialization
329 EXPECT_RESULT, Number::New(0));
330 }
331
332 { AbsentPropertyContext context;
333 context.Check("function x() { }; x",
334 1, // access
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100335 0,
Steve Blocka7e24c12009-10-30 11:49:00 +0000336 0,
337 EXPECT_RESULT);
338 }
339
340 { AbsentPropertyContext context;
341 context.Check("const x; x",
342 1, // access
343 2, // declaration + initialization
344 2, // declaration + initializetion
345 EXPECT_RESULT, Undefined());
346 }
347
348 { AbsentPropertyContext context;
349 context.Check("const x = 0; x",
350 1, // access
351 2, // declaration + initialization
352 2, // declaration + initialization
353 EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
354 }
355
356 { AbsentPropertyContext context;
357 context.Check("if (false) { var x = 0 }; x",
358 1, // access
359 1, // declaration
360 1, // declaration + initialization
361 EXPECT_RESULT, Undefined());
362 }
363}
364
365
366
367class AppearingPropertyContext: public DeclarationContext {
368 public:
369 enum State {
370 DECLARE,
371 INITIALIZE_IF_ASSIGN,
372 UNKNOWN
373 };
374
375 AppearingPropertyContext() : state_(DECLARE) { }
376
377 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100378 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000379 switch (state_) {
380 case DECLARE:
381 // Force declaration by returning that the
382 // property is absent.
383 state_ = INITIALIZE_IF_ASSIGN;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100384 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000385 case INITIALIZE_IF_ASSIGN:
386 // Return that the property is present so we only get the
387 // setter called when initializing with a value.
388 state_ = UNKNOWN;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100389 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000390 default:
391 CHECK(state_ == UNKNOWN);
392 break;
393 }
394 // Do the lookup in the object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100395 return v8::Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000396 }
397
398 private:
399 State state_;
400};
401
402
403TEST(Appearing) {
404 HandleScope scope;
405
406 { AppearingPropertyContext context;
407 context.Check("var x; x",
408 1, // access
409 1, // declaration
410 2, // declaration + initialization
411 EXPECT_RESULT, Undefined());
412 }
413
414 { AppearingPropertyContext context;
415 context.Check("var x = 0; x",
416 1, // access
417 2, // declaration + initialization
418 2, // declaration + initialization
419 EXPECT_RESULT, Number::New(0));
420 }
421
422 { AppearingPropertyContext context;
423 context.Check("function x() { }; x",
424 1, // access
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100425 0,
Steve Blocka7e24c12009-10-30 11:49:00 +0000426 0,
427 EXPECT_RESULT);
428 }
429
430 { AppearingPropertyContext context;
431 context.Check("const x; x",
432 0,
433 1, // declaration
434 2, // declaration + initialization
435 EXPECT_EXCEPTION); // x has already been declared!
436 }
437
438 { AppearingPropertyContext context;
439 context.Check("const x = 0; x",
440 0,
441 1, // declaration
442 2, // declaration + initialization
443 EXPECT_EXCEPTION); // x has already been declared!
444 }
445}
446
447
448
449class ReappearingPropertyContext: public DeclarationContext {
450 public:
451 enum State {
452 DECLARE,
453 DONT_DECLARE,
454 INITIALIZE,
455 UNKNOWN
456 };
457
458 ReappearingPropertyContext() : state_(DECLARE) { }
459
460 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100461 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000462 switch (state_) {
463 case DECLARE:
464 // Force the first declaration by returning that
465 // the property is absent.
466 state_ = DONT_DECLARE;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100467 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000468 case DONT_DECLARE:
469 // Ignore the second declaration by returning
470 // that the property is already there.
471 state_ = INITIALIZE;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100472 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 case INITIALIZE:
474 // Force an initialization by returning that
475 // the property is absent. This will make sure
476 // that the setter is called and it will not
477 // lead to redeclaration conflicts (yet).
478 state_ = UNKNOWN;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100479 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000480 default:
481 CHECK(state_ == UNKNOWN);
482 break;
483 }
484 // Do the lookup in the object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100485 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000486 }
487
488 private:
489 State state_;
490};
491
492
493TEST(Reappearing) {
494 HandleScope scope;
495
496 { ReappearingPropertyContext context;
497 context.Check("const x; var x = 0",
498 0,
499 2, // var declaration + const initialization
500 4, // 2 x declaration + 2 x initialization
501 EXPECT_EXCEPTION); // x has already been declared!
502 }
503}
504
505
506
507class ExistsInPrototypeContext: public DeclarationContext {
508 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100509 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000510 // Let it seem that the property exists in the prototype object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100511 return Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +0000512 }
513
514 // Use the prototype as the holder for the interceptors.
515 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
516 return function->PrototypeTemplate();
517 }
518};
519
520
521TEST(ExistsInPrototype) {
522 HandleScope scope;
523
524 // Sanity check to make sure that the holder of the interceptor
525 // really is the prototype object.
526 { ExistsInPrototypeContext context;
527 context.Check("this.x = 87; this.x",
528 0,
529 0,
530 0,
531 EXPECT_RESULT, Number::New(87));
532 }
533
534 { ExistsInPrototypeContext context;
535 context.Check("var x; x",
536 1, // get
537 0,
538 1, // declaration
539 EXPECT_EXCEPTION);
540 }
541
542 { ExistsInPrototypeContext context;
543 context.Check("var x = 0; x",
544 0,
545 0,
546 1, // declaration
547 EXPECT_RESULT, Number::New(0));
548 }
549
550 { ExistsInPrototypeContext context;
551 context.Check("const x; x",
552 0,
553 0,
554 1, // declaration
555 EXPECT_RESULT, Undefined());
556 }
557
558 { ExistsInPrototypeContext context;
559 context.Check("const x = 0; x",
560 0,
561 0,
562 1, // declaration
563 EXPECT_RESULT, Number::New(0));
564 }
565}
566
567
568
569class AbsentInPrototypeContext: public DeclarationContext {
570 protected:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100571 virtual v8::Handle<Integer> Query(Local<String> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000572 // Let it seem that the property is absent in the prototype object.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100573 return Handle<Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +0000574 }
575
576 // Use the prototype as the holder for the interceptors.
577 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
578 return function->PrototypeTemplate();
579 }
580};
581
582
583TEST(AbsentInPrototype) {
584 HandleScope scope;
585
586 { AbsentInPrototypeContext context;
587 context.Check("if (false) { var x = 0; }; x",
588 0,
589 0,
590 1, // declaration
591 EXPECT_RESULT, Undefined());
592 }
593}