OLD | NEW |
| (Empty) |
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 // - register a native accessor which is different from the build one | |
23 // (so that our tests will always fail if we don't end up in the 'fast' | |
24 // accessor) | |
25 // | |
26 // This doesn't work if the src function is inlined - as it is when | |
27 // --always-opt is enabled - since then every inlined functino is its own | |
28 // callsite. Hence most test will check for i::FLAG_always_opt. | |
29 #define WARMUP(src) "for(i = 0; i < 2; i++) { " src " } " | |
30 | |
31 static void NativePropertyAccessor( | |
32 const v8::FunctionCallbackInfo<v8::Value>& info) { | |
33 info.GetReturnValue().Set(v8_num(123)); | |
34 } | |
35 | |
36 } // anonymous namespace | |
37 | |
38 | |
39 // Build a simple "fast accessor" and verify that it is being called. | |
40 TEST(FastAccessor) { | |
41 if (i::FLAG_always_opt || i::FLAG_optimize_for_size) return; | |
42 | |
43 LocalContext env; | |
44 v8::Isolate* isolate = env->GetIsolate(); | |
45 v8::HandleScope scope(isolate); | |
46 | |
47 v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate); | |
48 | |
49 // Native accessor, bar, returns 123. | |
50 foo->PrototypeTemplate()->SetAccessorProperty( | |
51 v8_str("bar"), | |
52 v8::FunctionTemplate::New(isolate, NativePropertyAccessor)); | |
53 | |
54 // Fast accessor, barf, returns 124. | |
55 auto fab = v8::experimental::FastAccessorBuilder::New(isolate); | |
56 fab->ReturnValue(fab->IntegerConstant(124)); | |
57 foo->PrototypeTemplate()->SetAccessorProperty( | |
58 v8_str("barf"), v8::FunctionTemplate::NewWithFastHandler( | |
59 isolate, NativePropertyAccessor, fab)); | |
60 | |
61 // Install foo on the global object. | |
62 CHECK(env->Global() | |
63 ->Set(env.local(), v8_str("foo"), | |
64 foo->GetFunction(env.local()).ToLocalChecked()) | |
65 .FromJust()); | |
66 | |
67 // Wrap f.barf + IC warmup. | |
68 CompileRun( | |
69 "function barf() { f = new foo(); return f.barf }; " WARMUP("barf()")); | |
70 | |
71 ExpectInt32("f = new foo(); f.bar", 123); | |
72 ExpectInt32("f = new foo(); f.barf", 123); // First call in this call site. | |
73 ExpectInt32("barf()", 124); // Call via warmed-up callsite. | |
74 } | |
75 | |
76 | |
77 void AddInternalFieldAccessor(v8::Isolate* isolate, | |
78 v8::Local<v8::Template> templ, const char* name, | |
79 int field_no) { | |
80 auto builder = v8::experimental::FastAccessorBuilder::New(isolate); | |
81 builder->ReturnValue( | |
82 builder->LoadInternalField(builder->GetReceiver(), field_no)); | |
83 templ->SetAccessorProperty(v8_str(name), | |
84 v8::FunctionTemplate::NewWithFastHandler( | |
85 isolate, NativePropertyAccessor, builder)); | |
86 } | |
87 | |
88 | |
89 // "Fast" accessor that accesses an internal field. | |
90 TEST(FastAccessorWithInternalField) { | |
91 if (i::FLAG_always_opt || i::FLAG_optimize_for_size) return; | |
92 | |
93 LocalContext env; | |
94 v8::Isolate* isolate = env->GetIsolate(); | |
95 v8::HandleScope scope(isolate); | |
96 | |
97 v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate); | |
98 foo->SetInternalFieldCount(3); | |
99 AddInternalFieldAccessor(isolate, foo, "field0", 0); | |
100 AddInternalFieldAccessor(isolate, foo, "field1", 1); | |
101 AddInternalFieldAccessor(isolate, foo, "field2", 2); | |
102 | |
103 // Create an instance w/ 3 internal fields, put in a string, a Smi, nothing. | |
104 v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked(); | |
105 obj->SetInternalField(0, v8_str("Hi there!")); | |
106 obj->SetInternalField(1, v8::Integer::New(isolate, 4321)); | |
107 CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); | |
108 | |
109 // Warmup. | |
110 CompileRun("function field0() { return obj.field0 }; " WARMUP("field0()")); | |
111 CompileRun("function field1() { return obj.field1 }; " WARMUP("field1()")); | |
112 CompileRun("function field2() { return obj.field2 }; " WARMUP("field2()")); | |
113 | |
114 // Access fields. | |
115 ExpectString("field0()", "Hi there!"); | |
116 ExpectInt32("field1()", 4321); | |
117 ExpectUndefined("field2()"); | |
118 } | |
119 | |
120 | |
121 // "Fast" accessor with control flow via ...OrReturnNull methods. | |
122 TEST(FastAccessorOrReturnNull) { | |
123 if (i::FLAG_always_opt || i::FLAG_optimize_for_size) return; | |
124 | |
125 LocalContext env; | |
126 v8::Isolate* isolate = env->GetIsolate(); | |
127 v8::HandleScope scope(isolate); | |
128 | |
129 v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate); | |
130 foo->SetInternalFieldCount(2); | |
131 { | |
132 // accessor "nullcheck": Return null if field 0 is non-null object; else 5. | |
133 auto builder = v8::experimental::FastAccessorBuilder::New(isolate); | |
134 auto val = builder->LoadInternalField(builder->GetReceiver(), 0); | |
135 builder->CheckNotZeroOrReturnNull(val); | |
136 builder->ReturnValue(builder->IntegerConstant(5)); | |
137 foo->SetAccessorProperty(v8_str("nullcheck"), | |
138 v8::FunctionTemplate::NewWithFastHandler( | |
139 isolate, NativePropertyAccessor, builder)); | |
140 } | |
141 { | |
142 // accessor "maskcheck": Return null if field 1 has 3rd bit set. | |
143 auto builder = v8::experimental::FastAccessorBuilder::New(isolate); | |
144 auto val = builder->LoadInternalField(builder->GetReceiver(), 1); | |
145 builder->CheckFlagSetOrReturnNull(val, 0x4); | |
146 builder->ReturnValue(builder->IntegerConstant(42)); | |
147 foo->SetAccessorProperty(v8_str("maskcheck"), | |
148 v8::FunctionTemplate::NewWithFastHandler( | |
149 isolate, NativePropertyAccessor, builder)); | |
150 } | |
151 | |
152 // Create an instance. | |
153 v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked(); | |
154 CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); | |
155 | |
156 // CheckNotZeroOrReturnNull: | |
157 CompileRun( | |
158 "function nullcheck() { return obj.nullcheck }; " WARMUP("nullcheck()")); | |
159 obj->SetAlignedPointerInInternalField(0, /* anything != nullptr */ isolate); | |
160 ExpectInt32("nullcheck()", 5); | |
161 obj->SetAlignedPointerInInternalField(0, nullptr); | |
162 ExpectNull("nullcheck()"); | |
163 | |
164 // CheckFlagSetOrReturnNull: | |
165 CompileRun( | |
166 "function maskcheck() { return obj.maskcheck }; " WARMUP("maskcheck()")); | |
167 obj->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0xf0)); | |
168 ExpectInt32("maskcheck()", 42); | |
169 obj->SetAlignedPointerInInternalField(1, reinterpret_cast<void*>(0xfe)); | |
170 ExpectNull("maskcheck()"); | |
171 } | |
172 | |
173 | |
174 // "Fast" accessor with simple control flow via explicit labels. | |
175 TEST(FastAccessorControlFlowWithLabels) { | |
176 if (i::FLAG_always_opt || i::FLAG_optimize_for_size) return; | |
177 | |
178 LocalContext env; | |
179 v8::Isolate* isolate = env->GetIsolate(); | |
180 v8::HandleScope scope(isolate); | |
181 | |
182 v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate); | |
183 foo->SetInternalFieldCount(1); | |
184 { | |
185 // accessor isnull: 0 for nullptr, else 1. | |
186 auto builder = v8::experimental::FastAccessorBuilder::New(isolate); | |
187 auto label = builder->MakeLabel(); | |
188 auto val = builder->LoadInternalField(builder->GetReceiver(), 0); | |
189 builder->CheckNotZeroOrJump(val, label); | |
190 builder->ReturnValue(builder->IntegerConstant(0)); | |
191 builder->SetLabel(label); | |
192 builder->ReturnValue(builder->IntegerConstant(1)); | |
193 foo->SetAccessorProperty(v8_str("isnull"), | |
194 v8::FunctionTemplate::NewWithFastHandler( | |
195 isolate, NativePropertyAccessor, builder)); | |
196 } | |
197 | |
198 // Create an instance. | |
199 v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked(); | |
200 CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); | |
201 | |
202 // CheckNotZeroOrReturnNull: | |
203 CompileRun("function isnull() { return obj.isnull }; " WARMUP("isnull()")); | |
204 obj->SetAlignedPointerInInternalField(0, /* anything != nullptr */ isolate); | |
205 ExpectInt32("isnull()", 1); | |
206 obj->SetAlignedPointerInInternalField(0, nullptr); | |
207 ExpectInt32("isnull()", 0); | |
208 } | |
209 | |
210 | |
211 // "Fast" accessor, loading things. | |
212 TEST(FastAccessorLoad) { | |
213 if (i::FLAG_always_opt || i::FLAG_optimize_for_size) return; | |
214 | |
215 LocalContext env; | |
216 v8::Isolate* isolate = env->GetIsolate(); | |
217 v8::HandleScope scope(isolate); | |
218 | |
219 v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate); | |
220 foo->SetInternalFieldCount(1); | |
221 | |
222 // Internal field 0 is a pointer to a C++ data structure that we wish to load | |
223 // field values from. | |
224 struct { | |
225 size_t intval; | |
226 v8::Local<v8::String> v8val; | |
227 } val = {54321, v8_str("Hello")}; | |
228 | |
229 { | |
230 // accessor intisnonzero | |
231 int intval_offset = | |
232 static_cast<int>(reinterpret_cast<intptr_t>(&val.intval) - | |
233 reinterpret_cast<intptr_t>(&val)); | |
234 auto builder = v8::experimental::FastAccessorBuilder::New(isolate); | |
235 auto label = builder->MakeLabel(); | |
236 auto val = builder->LoadValue( | |
237 builder->LoadInternalField(builder->GetReceiver(), 0), intval_offset); | |
238 builder->CheckNotZeroOrJump(val, label); | |
239 builder->ReturnValue(builder->IntegerConstant(0)); | |
240 builder->SetLabel(label); | |
241 builder->ReturnValue(builder->IntegerConstant(1)); | |
242 foo->SetAccessorProperty(v8_str("nonzero"), | |
243 v8::FunctionTemplate::NewWithFastHandler( | |
244 isolate, NativePropertyAccessor, builder)); | |
245 } | |
246 { | |
247 // accessor loadval | |
248 int v8val_offset = static_cast<int>(reinterpret_cast<intptr_t>(&val.v8val) - | |
249 reinterpret_cast<intptr_t>(&val)); | |
250 auto builder = v8::experimental::FastAccessorBuilder::New(isolate); | |
251 builder->ReturnValue(builder->LoadObject( | |
252 builder->LoadInternalField(builder->GetReceiver(), 0), v8val_offset)); | |
253 foo->SetAccessorProperty(v8_str("loadval"), | |
254 v8::FunctionTemplate::NewWithFastHandler( | |
255 isolate, NativePropertyAccessor, builder)); | |
256 } | |
257 | |
258 // Create an instance. | |
259 v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked(); | |
260 obj->SetAlignedPointerInInternalField(0, &val); | |
261 CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); | |
262 | |
263 // Access val.intval: | |
264 CompileRun("function nonzero() { return obj.nonzero }; " WARMUP("nonzero()")); | |
265 ExpectInt32("nonzero()", 1); | |
266 val.intval = 0; | |
267 ExpectInt32("nonzero()", 0); | |
268 val.intval = 27; | |
269 ExpectInt32("nonzero()", 1); | |
270 | |
271 // Access val.v8val: | |
272 CompileRun("function loadval() { return obj.loadval }; " WARMUP("loadval()")); | |
273 ExpectString("loadval()", "Hello"); | |
274 } | |
OLD | NEW |