• 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_; }
query_count() const66   int query_count() const { return query_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<Integer> Query(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<Integer> HandleQuery(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 query_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), query_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                                                &HandleQuery,
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 query,Expectations expectations,v8::Handle<Value> value)126 void DeclarationContext::Check(const char* source,
127                                int get, int set, int query,
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   HEAP->CollectGarbage(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(query, query_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 
HandleQuery(Local<String> key,const AccessorInfo & info)173 v8::Handle<Integer> DeclarationContext::HandleQuery(Local<String> key,
174                                                     const AccessorInfo& info) {
175   DeclarationContext* context = GetInstance(info);
176   context->query_count_++;
177   return context->Query(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 
Query(Local<String> key)197 v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
198   return v8::Handle<Integer>();
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                   0,
227                   0,
228                   EXPECT_RESULT);
229   }
230 
231   { DeclarationContext context;
232     context.Check("const x; x",
233                   1,  // access
234                   2,  // declaration + initialization
235                   1,  // declaration
236                   EXPECT_RESULT, Undefined());
237   }
238 
239   { DeclarationContext context;
240     context.Check("const x = 0; x",
241                   1,  // access
242                   2,  // declaration + initialization
243                   1,  // declaration
244                   EXPECT_RESULT, Undefined());  // SB 0 - BUG 1213579
245   }
246 }
247 
248 
249 
250 class PresentPropertyContext: public DeclarationContext {
251  protected:
Query(Local<String> key)252   virtual v8::Handle<Integer> Query(Local<String> key) {
253     return Integer::New(v8::None);
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                   0,
282                   0,
283                   EXPECT_RESULT);
284   }
285 
286   { PresentPropertyContext context;
287     context.Check("const x; x",
288                   1,  // access
289                   1,  // initialization
290                   1,  // (re-)declaration
291                   EXPECT_RESULT, Undefined());
292   }
293 
294   { PresentPropertyContext context;
295     context.Check("const x = 0; x",
296                   1,  // access
297                   1,  // initialization
298                   1,  // (re-)declaration
299                   EXPECT_RESULT, Number::New(0));
300   }
301 }
302 
303 
304 
305 class AbsentPropertyContext: public DeclarationContext {
306  protected:
Query(Local<String> key)307   virtual v8::Handle<Integer> Query(Local<String> key) {
308     return v8::Handle<Integer>();
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                   1,  // declaration
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                   0,
336                   0,
337                   EXPECT_RESULT);
338   }
339 
340   { AbsentPropertyContext context;
341     context.Check("const x; x",
342                   1,  // access
343                   2,  // declaration + initialization
344                   1,  // declaration
345                   EXPECT_RESULT, Undefined());
346   }
347 
348   { AbsentPropertyContext context;
349     context.Check("const x = 0; x",
350                   1,  // access
351                   2,  // declaration + initialization
352                   1,  // declaration
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:
Query(Local<String> key)378   virtual v8::Handle<Integer> Query(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 Handle<Integer>();
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 Integer::New(v8::None);
390       default:
391         CHECK(state_ == UNKNOWN);
392         break;
393     }
394     // Do the lookup in the object.
395     return v8::Handle<Integer>();
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                   0,
426                   0,
427                   EXPECT_RESULT);
428   }
429 
430   { AppearingPropertyContext context;
431     context.Check("const x; x",
432                   1,  // access
433                   2,  // declaration + initialization
434                   1,  // declaration
435                   EXPECT_RESULT, Undefined());
436   }
437 
438   { AppearingPropertyContext context;
439     context.Check("const x = 0; x",
440                   1,  // access
441                   2,  // declaration + initialization
442                   1,  // declaration
443                   EXPECT_RESULT, Undefined());
444                   // Result is undefined because declaration succeeded but
445                   // initialization to 0 failed (due to context behavior).
446   }
447 }
448 
449 
450 
451 class ReappearingPropertyContext: public DeclarationContext {
452  public:
453   enum State {
454     DECLARE,
455     DONT_DECLARE,
456     INITIALIZE,
457     UNKNOWN
458   };
459 
ReappearingPropertyContext()460   ReappearingPropertyContext() : state_(DECLARE) { }
461 
462  protected:
Query(Local<String> key)463   virtual v8::Handle<Integer> Query(Local<String> key) {
464     switch (state_) {
465       case DECLARE:
466         // Force the first declaration by returning that
467         // the property is absent.
468         state_ = DONT_DECLARE;
469         return Handle<Integer>();
470       case DONT_DECLARE:
471         // Ignore the second declaration by returning
472         // that the property is already there.
473         state_ = INITIALIZE;
474         return Integer::New(v8::None);
475       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;
481         return Handle<Integer>();
482       default:
483         CHECK(state_ == UNKNOWN);
484         break;
485     }
486     // Do the lookup in the object.
487     return Handle<Integer>();
488   }
489 
490  private:
491   State state_;
492 };
493 
494 
TEST(Reappearing)495 TEST(Reappearing) {
496   HandleScope scope;
497 
498   { ReappearingPropertyContext context;
499     context.Check("const x; var x = 0",
500                   0,
501                   3,  // const declaration+initialization, var initialization
502                   3,  // 2 x declaration + var initialization
503                   EXPECT_RESULT, Undefined());
504   }
505 }
506 
507 
508 
509 class ExistsInPrototypeContext: public DeclarationContext {
510  protected:
Query(Local<String> key)511   virtual v8::Handle<Integer> Query(Local<String> key) {
512     // Let it seem that the property exists in the prototype object.
513     return Integer::New(v8::None);
514   }
515 
516   // Use the prototype as the holder for the interceptors.
GetHolder(Local<FunctionTemplate> function)517   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
518     return function->PrototypeTemplate();
519   }
520 };
521 
522 
TEST(ExistsInPrototype)523 TEST(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 
571 class AbsentInPrototypeContext: public DeclarationContext {
572  protected:
Query(Local<String> key)573   virtual v8::Handle<Integer> Query(Local<String> key) {
574     // Let it seem that the property is absent in the prototype object.
575     return Handle<Integer>();
576   }
577 
578   // Use the prototype as the holder for the interceptors.
GetHolder(Local<FunctionTemplate> function)579   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
580     return function->PrototypeTemplate();
581   }
582 };
583 
584 
TEST(AbsentInPrototype)585 TEST(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 }
596