• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdlib.h>
6 
7 #include "include/v8.h"
8 #include "include/v8-experimental.h"
9 
10 #include "src/api.h"
11 #include "test/cctest/cctest.h"
12 
13 namespace {
14 
15 // These tests mean to exercise v8::FastAccessorBuilder. Since initially the
16 // "native" accessor will get called, we need to "warmup" any accessor first,
17 // to make sure we're actually testing the v8::FastAccessorBuilder result.
18 // To accomplish this, we will
19 // - call each accesssor N times before the actual test.
20 // - wrap that call in a function, so that all such calls will go
21 //   through a single call site.
22 // - bloat that function with a very long comment to prevent its inlining.
23 // - register a native accessor which is different from the build one
24 //   (so that our tests will always fail if we don't end up in the 'fast'
25 //    accessor).
26 //
27 // FN_WARMUP(name, src) define a JS function "name" with body "src".
28 // It adds the INLINE_SPOILER to prevent inlining and will call name()
29 // repeatedly to guarantee it's "warm".
30 //
31 // Use:
32 //   CompileRun(FN_WARMUP("fn", "return something();"));
33 //   ExpectXXX("fn(1234)", 5678);
34 
35 #define INLINE_SPOILER                                           \
36   " /* "                                                         \
37   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
38   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
39   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
40   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
41   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
42   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
43   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
44   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
45   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
46   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
47   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
48   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
49   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
50   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
51   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
52   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
53   "*/ "  // 16 lines * 64 'X' =~ 1024 character comment.
54 #define FN_WARMUP(name, src)                  \
55   "function " name "() { " src INLINE_SPOILER \
56   " }; for(i = 0; i < 2; i++) { " name "() } "
57 
NativePropertyAccessor(const v8::FunctionCallbackInfo<v8::Value> & info)58 static void NativePropertyAccessor(
59     const v8::FunctionCallbackInfo<v8::Value>& info) {
60   info.GetReturnValue().Set(v8_num(123));
61 }
62 
63 }  // anonymous namespace
64 
65 
66 // Build a simple "fast accessor" and verify that it is being called.
TEST(FastAccessor)67 TEST(FastAccessor) {
68   LocalContext env;
69   v8::Isolate* isolate = env->GetIsolate();
70   v8::HandleScope scope(isolate);
71 
72   v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate);
73 
74   // Native accessor, bar, returns 123.
75   foo->PrototypeTemplate()->SetAccessorProperty(
76       v8_str("bar"),
77       v8::FunctionTemplate::New(isolate, NativePropertyAccessor));
78 
79   // Fast accessor, barf, returns 124.
80   auto fab = v8::experimental::FastAccessorBuilder::New(isolate);
81   fab->ReturnValue(fab->IntegerConstant(124));
82   foo->PrototypeTemplate()->SetAccessorProperty(
83       v8_str("barf"), v8::FunctionTemplate::NewWithFastHandler(
84                           isolate, NativePropertyAccessor, fab));
85 
86   // Install foo on the global object.
87   CHECK(env->Global()
88             ->Set(env.local(), v8_str("foo"),
89                   foo->GetFunction(env.local()).ToLocalChecked())
90             .FromJust());
91 
92   // Wrap f.barf + IC warmup.
93   CompileRun(FN_WARMUP("barf", "f = new foo(); return f.barf"));
94 
95   ExpectInt32("f = new foo(); f.bar", 123);
96   ExpectInt32("f = new foo(); f.barf", 123);  // First call in this call site.
97   ExpectInt32("barf()", 124);                 // Call via warmed-up callsite.
98 }
99 
100 
AddInternalFieldAccessor(v8::Isolate * isolate,v8::Local<v8::Template> templ,const char * name,int field_no)101 void AddInternalFieldAccessor(v8::Isolate* isolate,
102                               v8::Local<v8::Template> templ, const char* name,
103                               int field_no) {
104   auto builder = v8::experimental::FastAccessorBuilder::New(isolate);
105   builder->ReturnValue(
106       builder->LoadInternalField(builder->GetReceiver(), field_no));
107   templ->SetAccessorProperty(v8_str(name),
108                              v8::FunctionTemplate::NewWithFastHandler(
109                                  isolate, NativePropertyAccessor, builder));
110 }
111 
112 
113 // "Fast" accessor that accesses an internal field.
TEST(FastAccessorWithInternalField)114 TEST(FastAccessorWithInternalField) {
115   LocalContext env;
116   v8::Isolate* isolate = env->GetIsolate();
117   v8::HandleScope scope(isolate);
118 
119   v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate);
120   foo->SetInternalFieldCount(3);
121   AddInternalFieldAccessor(isolate, foo, "field0", 0);
122   AddInternalFieldAccessor(isolate, foo, "field1", 1);
123   AddInternalFieldAccessor(isolate, foo, "field2", 2);
124 
125   // Create an instance w/ 3 internal fields, put in a string, a Smi, nothing.
126   v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked();
127   obj->SetInternalField(0, v8_str("Hi there!"));
128   obj->SetInternalField(1, v8::Integer::New(isolate, 4321));
129   CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
130 
131   // Warmup.
132   CompileRun(FN_WARMUP("field0", "return obj.field0"));
133   CompileRun(FN_WARMUP("field1", "return obj.field1"));
134   CompileRun(FN_WARMUP("field2", "return obj.field2"));
135 
136   // Access fields.
137   ExpectString("field0()", "Hi there!");
138   ExpectInt32("field1()", 4321);
139   ExpectUndefined("field2()");
140 }
141 
142 
143 // "Fast" accessor with control flow via ...OrReturnNull methods.
TEST(FastAccessorOrReturnNull)144 TEST(FastAccessorOrReturnNull) {
145   LocalContext env;
146   v8::Isolate* isolate = env->GetIsolate();
147   v8::HandleScope scope(isolate);
148 
149   v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate);
150   foo->SetInternalFieldCount(2);
151   {
152     // accessor "nullcheck": Return null if field 0 is non-null object; else 5.
153     auto builder = v8::experimental::FastAccessorBuilder::New(isolate);
154     auto val = builder->LoadInternalField(builder->GetReceiver(), 0);
155     builder->CheckNotZeroOrReturnNull(val);
156     builder->ReturnValue(builder->IntegerConstant(5));
157     foo->SetAccessorProperty(v8_str("nullcheck"),
158                              v8::FunctionTemplate::NewWithFastHandler(
159                                  isolate, NativePropertyAccessor, builder));
160   }
161   {
162     // accessor "maskcheck": Return null if field 1 has 3rd bit set.
163     auto builder = v8::experimental::FastAccessorBuilder::New(isolate);
164     auto val = builder->LoadInternalField(builder->GetReceiver(), 1);
165     builder->CheckFlagSetOrReturnNull(val, 0x4);
166     builder->ReturnValue(builder->IntegerConstant(42));
167     foo->SetAccessorProperty(v8_str("maskcheck"),
168                              v8::FunctionTemplate::NewWithFastHandler(
169                                  isolate, NativePropertyAccessor, builder));
170   }
171 
172   // Create an instance.
173   v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked();
174   CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
175 
176   // CheckNotZeroOrReturnNull:
177   CompileRun(FN_WARMUP("nullcheck", "return obj.nullcheck"));
178   obj->SetAlignedPointerInInternalField(0, /* anything != nullptr */ isolate);
179   ExpectInt32("nullcheck()", 5);
180   obj->SetAlignedPointerInInternalField(0, nullptr);
181   ExpectNull("nullcheck()");
182 
183   // CheckFlagSetOrReturnNull:
184   CompileRun(FN_WARMUP("maskcheck", "return obj.maskcheck"));
185   obj->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0xf0));
186   ExpectInt32("maskcheck()", 42);
187   obj->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0xfe));
188   ExpectNull("maskcheck()");
189 }
190 
191 
192 // "Fast" accessor with simple control flow via explicit labels.
TEST(FastAccessorControlFlowWithLabels)193 TEST(FastAccessorControlFlowWithLabels) {
194   LocalContext env;
195   v8::Isolate* isolate = env->GetIsolate();
196   v8::HandleScope scope(isolate);
197 
198   v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate);
199   foo->SetInternalFieldCount(1);
200   {
201     // accessor isnull: 0 for nullptr, else 1.
202     auto builder = v8::experimental::FastAccessorBuilder::New(isolate);
203     auto label = builder->MakeLabel();
204     auto val = builder->LoadInternalField(builder->GetReceiver(), 0);
205     builder->CheckNotZeroOrJump(val, label);
206     builder->ReturnValue(builder->IntegerConstant(0));
207     builder->SetLabel(label);
208     builder->ReturnValue(builder->IntegerConstant(1));
209     foo->SetAccessorProperty(v8_str("isnull"),
210                              v8::FunctionTemplate::NewWithFastHandler(
211                                  isolate, NativePropertyAccessor, builder));
212   }
213 
214   // Create an instance.
215   v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked();
216   CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
217 
218   // CheckNotZeroOrReturnNull:
219   CompileRun(FN_WARMUP("isnull", "return obj.isnull"));
220   obj->SetAlignedPointerInInternalField(0, /* anything != nullptr */ isolate);
221   ExpectInt32("isnull()", 1);
222   obj->SetAlignedPointerInInternalField(0, nullptr);
223   ExpectInt32("isnull()", 0);
224 }
225 
226 
227 // "Fast" accessor, loading things.
TEST(FastAccessorLoad)228 TEST(FastAccessorLoad) {
229   LocalContext env;
230   v8::Isolate* isolate = env->GetIsolate();
231   v8::HandleScope scope(isolate);
232 
233   v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate);
234   foo->SetInternalFieldCount(1);
235 
236   // Internal field 0 is a pointer to a C++ data structure that we wish to load
237   // field values from.
238   struct {
239     size_t intval;
240     v8::Local<v8::String> v8val;
241   } val = {54321, v8_str("Hello")};
242 
243   {
244     // accessor intisnonzero
245     int intval_offset =
246         static_cast<int>(reinterpret_cast<intptr_t>(&val.intval) -
247                          reinterpret_cast<intptr_t>(&val));
248     auto builder = v8::experimental::FastAccessorBuilder::New(isolate);
249     auto label = builder->MakeLabel();
250     auto val = builder->LoadValue(
251         builder->LoadInternalField(builder->GetReceiver(), 0), intval_offset);
252     builder->CheckNotZeroOrJump(val, label);
253     builder->ReturnValue(builder->IntegerConstant(0));
254     builder->SetLabel(label);
255     builder->ReturnValue(builder->IntegerConstant(1));
256     foo->SetAccessorProperty(v8_str("nonzero"),
257                              v8::FunctionTemplate::NewWithFastHandler(
258                                  isolate, NativePropertyAccessor, builder));
259   }
260   {
261     // accessor loadval
262     int v8val_offset = static_cast<int>(reinterpret_cast<intptr_t>(&val.v8val) -
263                                         reinterpret_cast<intptr_t>(&val));
264     auto builder = v8::experimental::FastAccessorBuilder::New(isolate);
265     builder->ReturnValue(builder->LoadObject(
266         builder->LoadInternalField(builder->GetReceiver(), 0), v8val_offset));
267     foo->SetAccessorProperty(v8_str("loadval"),
268                              v8::FunctionTemplate::NewWithFastHandler(
269                                  isolate, NativePropertyAccessor, builder));
270   }
271 
272   // Create an instance.
273   v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked();
274   obj->SetAlignedPointerInInternalField(0, &val);
275   CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust());
276 
277   // Access val.intval:
278   CompileRun(FN_WARMUP("nonzero", "return obj.nonzero"));
279   ExpectInt32("nonzero()", 1);
280   val.intval = 0;
281   ExpectInt32("nonzero()", 0);
282   val.intval = 27;
283   ExpectInt32("nonzero()", 1);
284 
285   // Access val.v8val:
286   CompileRun(FN_WARMUP("loadval", "return obj.loadval"));
287   ExpectString("loadval()", "Hello");
288 }
289