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