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