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