• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
35 using namespace v8;
36 
37 
38 enum 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.
48 class DeclarationContext {
49  public:
50   DeclarationContext();
51 
~DeclarationContext()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 
get_count() const64   int get_count() const { return get_count_; }
set_count() const65   int set_count() const { return set_count_; }
has_count() const66   int has_count() const { return has_count_; }
67 
68  protected:
69   virtual v8::Handle<Value> Get(Local<String> key);
70   virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
71   virtual v8::Handle<Boolean> Has(Local<String> key);
72 
73   void InitializeIfNeeded();
74 
75   // Get the holder for the interceptor. Default to the instance template
76   // but may be overwritten.
GetHolder(Local<FunctionTemplate> function)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);
88   static v8::Handle<Boolean> HandleHas(Local<String> key,
89                                        const AccessorInfo& info);
90 
91  private:
92   bool is_initialized_;
93   Persistent<Context> context_;
94   Local<String> property_;
95 
96   int get_count_;
97   int set_count_;
98   int has_count_;
99 
100   static DeclarationContext* GetInstance(const AccessorInfo& info);
101 };
102 
103 
DeclarationContext()104 DeclarationContext::DeclarationContext()
105     : is_initialized_(false), get_count_(0), set_count_(0), has_count_(0) {
106   // Do nothing.
107 }
108 
109 
InitializeIfNeeded()110 void 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,
117                                                &HandleHas,
118                                                0, 0,
119                                                data);
120   context_ = Context::New(0, function->InstanceTemplate(), Local<Value>());
121   context_->Enter();
122   is_initialized_ = true;
123 }
124 
125 
Check(const char * source,int get,int set,int has,Expectations expectations,v8::Handle<Value> value)126 void DeclarationContext::Check(const char* source,
127                                int get, int set, int has,
128                                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.
133   v8::internal::Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
134   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());
140   CHECK_EQ(has, has_count());
141   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 
HandleGet(Local<String> key,const AccessorInfo & info)156 v8::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 
HandleSet(Local<String> key,Local<Value> value,const AccessorInfo & info)164 v8::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 
HandleHas(Local<String> key,const AccessorInfo & info)173 v8::Handle<Boolean> DeclarationContext::HandleHas(Local<String> key,
174                                                   const AccessorInfo& info) {
175   DeclarationContext* context = GetInstance(info);
176   context->has_count_++;
177   return context->Has(key);
178 }
179 
180 
GetInstance(const AccessorInfo & info)181 DeclarationContext* DeclarationContext::GetInstance(const AccessorInfo& info) {
182   return static_cast<DeclarationContext*>(External::Unwrap(info.Data()));
183 }
184 
185 
Get(Local<String> key)186 v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
187   return v8::Handle<Value>();
188 }
189 
190 
Set(Local<String> key,Local<Value> value)191 v8::Handle<Value> DeclarationContext::Set(Local<String> key,
192                                           Local<Value> value) {
193   return v8::Handle<Value>();
194 }
195 
196 
Has(Local<String> key)197 v8::Handle<Boolean> DeclarationContext::Has(Local<String> key) {
198   return v8::Handle<Boolean>();
199 }
200 
201 
202 // Test global declaration of a property the interceptor doesn't know
203 // about and doesn't handle.
TEST(Unknown)204 TEST(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
226                   1,  // declaration
227                   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 
250 class PresentPropertyContext: public DeclarationContext {
251  protected:
Has(Local<String> key)252   virtual v8::Handle<Boolean> Has(Local<String> key) {
253     return True();
254   }
255 };
256 
257 
258 
TEST(Present)259 TEST(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
281                   1,  // declaration
282                   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 
305 class AbsentPropertyContext: public DeclarationContext {
306  protected:
Has(Local<String> key)307   virtual v8::Handle<Boolean> Has(Local<String> key) {
308     return False();
309   }
310 };
311 
312 
TEST(Absent)313 TEST(Absent) {
314   HandleScope scope;
315 
316   { AbsentPropertyContext context;
317     context.Check("var x; x",
318                   1,  // access
319                   2,  // declaration + initialization
320                   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
335                   1,  // declaration
336                   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 
367 class AppearingPropertyContext: public DeclarationContext {
368  public:
369   enum State {
370     DECLARE,
371     INITIALIZE_IF_ASSIGN,
372     UNKNOWN
373   };
374 
AppearingPropertyContext()375   AppearingPropertyContext() : state_(DECLARE) { }
376 
377  protected:
Has(Local<String> key)378   virtual v8::Handle<Boolean> Has(Local<String> key) {
379     switch (state_) {
380       case DECLARE:
381         // Force declaration by returning that the
382         // property is absent.
383         state_ = INITIALIZE_IF_ASSIGN;
384         return False();
385       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;
389         return True();
390       default:
391         CHECK(state_ == UNKNOWN);
392         break;
393     }
394     // Do the lookup in the object.
395     return v8::Local<Boolean>();
396   }
397 
398  private:
399   State state_;
400 };
401 
402 
TEST(Appearing)403 TEST(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
425                   1,  // declaration
426                   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 
449 class ReappearingPropertyContext: public DeclarationContext {
450  public:
451   enum State {
452     DECLARE,
453     DONT_DECLARE,
454     INITIALIZE,
455     UNKNOWN
456   };
457 
ReappearingPropertyContext()458   ReappearingPropertyContext() : state_(DECLARE) { }
459 
460  protected:
Has(Local<String> key)461   virtual v8::Handle<Boolean> Has(Local<String> key) {
462     switch (state_) {
463       case DECLARE:
464         // Force the first declaration by returning that
465         // the property is absent.
466         state_ = DONT_DECLARE;
467         return False();
468       case DONT_DECLARE:
469         // Ignore the second declaration by returning
470         // that the property is already there.
471         state_ = INITIALIZE;
472         return True();
473       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;
479         return False();
480       default:
481         CHECK(state_ == UNKNOWN);
482         break;
483     }
484     // Do the lookup in the object.
485     return v8::Local<Boolean>();
486   }
487 
488  private:
489   State state_;
490 };
491 
492 
TEST(Reappearing)493 TEST(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 
507 class ExistsInPrototypeContext: public DeclarationContext {
508  protected:
Has(Local<String> key)509   virtual v8::Handle<Boolean> Has(Local<String> key) {
510     // Let it seem that the property exists in the prototype object.
511     return True();
512   }
513 
514   // Use the prototype as the holder for the interceptors.
GetHolder(Local<FunctionTemplate> function)515   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
516     return function->PrototypeTemplate();
517   }
518 };
519 
520 
TEST(ExistsInPrototype)521 TEST(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 
569 class AbsentInPrototypeContext: public DeclarationContext {
570  protected:
Has(Local<String> key)571   virtual v8::Handle<Boolean> Has(Local<String> key) {
572     // Let it seem that the property is absent in the prototype object.
573     return False();
574   }
575 
576   // Use the prototype as the holder for the interceptors.
GetHolder(Local<FunctionTemplate> function)577   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
578     return function->PrototypeTemplate();
579   }
580 };
581 
582 
TEST(AbsentInPrototype)583 TEST(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 }
594