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 |