1// Copyright 2006-2008 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include <stdlib.h> 29#include <wchar.h> // wint_t 30 31#include "v8.h" 32 33#include "compiler.h" 34#include "execution.h" 35#include "factory.h" 36#include "platform.h" 37#include "top.h" 38#include "cctest.h" 39 40using namespace v8::internal; 41 42static v8::Persistent<v8::Context> env; 43 44// --- P r i n t E x t e n s i o n --- 45 46class PrintExtension : public v8::Extension { 47 public: 48 PrintExtension() : v8::Extension("v8/print", kSource) { } 49 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 50 v8::Handle<v8::String> name); 51 static v8::Handle<v8::Value> Print(const v8::Arguments& args); 52 private: 53 static const char* kSource; 54}; 55 56 57const char* PrintExtension::kSource = "native function print();"; 58 59 60v8::Handle<v8::FunctionTemplate> PrintExtension::GetNativeFunction( 61 v8::Handle<v8::String> str) { 62 return v8::FunctionTemplate::New(PrintExtension::Print); 63} 64 65 66v8::Handle<v8::Value> PrintExtension::Print(const v8::Arguments& args) { 67 for (int i = 0; i < args.Length(); i++) { 68 if (i != 0) printf(" "); 69 v8::HandleScope scope; 70 v8::Handle<v8::Value> arg = args[i]; 71 v8::Handle<v8::String> string_obj = arg->ToString(); 72 if (string_obj.IsEmpty()) return string_obj; 73 int length = string_obj->Length(); 74 uint16_t* string = NewArray<uint16_t>(length + 1); 75 string_obj->Write(string); 76 for (int j = 0; j < length; j++) 77 printf("%lc", static_cast<wint_t>(string[j])); 78 DeleteArray(string); 79 } 80 printf("\n"); 81 return v8::Undefined(); 82} 83 84 85static PrintExtension kPrintExtension; 86v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension); 87 88 89static void InitializeVM() { 90 if (env.IsEmpty()) { 91 v8::HandleScope scope; 92 const char* extensions[] = { "v8/print", "v8/gc" }; 93 v8::ExtensionConfiguration config(2, extensions); 94 env = v8::Context::New(&config); 95 } 96 v8::HandleScope scope; 97 env->Enter(); 98} 99 100 101static Object* GetGlobalProperty(const char* name) { 102 Handle<String> symbol = Factory::LookupAsciiSymbol(name); 103 return Top::context()->global()->GetProperty(*symbol); 104} 105 106 107static void SetGlobalProperty(const char* name, Object* value) { 108 Handle<Object> object(value); 109 Handle<String> symbol = Factory::LookupAsciiSymbol(name); 110 Handle<JSObject> global(Top::context()->global()); 111 SetProperty(global, symbol, object, NONE); 112} 113 114 115static Handle<JSFunction> Compile(const char* source) { 116 Handle<String> source_code(Factory::NewStringFromUtf8(CStrVector(source))); 117 Handle<JSFunction> boilerplate = Compiler::Compile(source_code, 118 Handle<String>(), 119 0, 120 0, 121 NULL, 122 NULL, 123 Handle<String>::null(), 124 NOT_NATIVES_CODE); 125 return Factory::NewFunctionFromBoilerplate(boilerplate, 126 Top::global_context()); 127} 128 129 130static double Inc(int x) { 131 const char* source = "result = %d + 1;"; 132 EmbeddedVector<char, 512> buffer; 133 OS::SNPrintF(buffer, source, x); 134 135 Handle<JSFunction> fun = Compile(buffer.start()); 136 if (fun.is_null()) return -1; 137 138 bool has_pending_exception; 139 Handle<JSObject> global(Top::context()->global()); 140 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 141 CHECK(!has_pending_exception); 142 return GetGlobalProperty("result")->Number(); 143} 144 145 146TEST(Inc) { 147 InitializeVM(); 148 v8::HandleScope scope; 149 CHECK_EQ(4.0, Inc(3)); 150} 151 152 153static double Add(int x, int y) { 154 Handle<JSFunction> fun = Compile("result = x + y;"); 155 if (fun.is_null()) return -1; 156 157 SetGlobalProperty("x", Smi::FromInt(x)); 158 SetGlobalProperty("y", Smi::FromInt(y)); 159 bool has_pending_exception; 160 Handle<JSObject> global(Top::context()->global()); 161 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 162 CHECK(!has_pending_exception); 163 return GetGlobalProperty("result")->Number(); 164} 165 166 167TEST(Add) { 168 InitializeVM(); 169 v8::HandleScope scope; 170 CHECK_EQ(5.0, Add(2, 3)); 171} 172 173 174static double Abs(int x) { 175 Handle<JSFunction> fun = Compile("if (x < 0) result = -x; else result = x;"); 176 if (fun.is_null()) return -1; 177 178 SetGlobalProperty("x", Smi::FromInt(x)); 179 bool has_pending_exception; 180 Handle<JSObject> global(Top::context()->global()); 181 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 182 CHECK(!has_pending_exception); 183 return GetGlobalProperty("result")->Number(); 184} 185 186 187TEST(Abs) { 188 InitializeVM(); 189 v8::HandleScope scope; 190 CHECK_EQ(3.0, Abs(-3)); 191} 192 193 194static double Sum(int n) { 195 Handle<JSFunction> fun = 196 Compile("s = 0; while (n > 0) { s += n; n -= 1; }; result = s;"); 197 if (fun.is_null()) return -1; 198 199 SetGlobalProperty("n", Smi::FromInt(n)); 200 bool has_pending_exception; 201 Handle<JSObject> global(Top::context()->global()); 202 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 203 CHECK(!has_pending_exception); 204 return GetGlobalProperty("result")->Number(); 205} 206 207 208TEST(Sum) { 209 InitializeVM(); 210 v8::HandleScope scope; 211 CHECK_EQ(5050.0, Sum(100)); 212} 213 214 215TEST(Print) { 216 InitializeVM(); 217 v8::HandleScope scope; 218 const char* source = "for (n = 0; n < 100; ++n) print(n, 1, 2);"; 219 Handle<JSFunction> fun = Compile(source); 220 if (fun.is_null()) return; 221 bool has_pending_exception; 222 Handle<JSObject> global(Top::context()->global()); 223 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 224 CHECK(!has_pending_exception); 225} 226 227 228// The following test method stems from my coding efforts today. It 229// tests all the functionality I have added to the compiler today 230TEST(Stuff) { 231 InitializeVM(); 232 v8::HandleScope scope; 233 const char* source = 234 "r = 0;\n" 235 "a = new Object;\n" 236 "if (a == a) r+=1;\n" // 1 237 "if (a != new Object()) r+=2;\n" // 2 238 "a.x = 42;\n" 239 "if (a.x == 42) r+=4;\n" // 4 240 "function foo() { var x = 87; return x; }\n" 241 "if (foo() == 87) r+=8;\n" // 8 242 "function bar() { var x; x = 99; return x; }\n" 243 "if (bar() == 99) r+=16;\n" // 16 244 "function baz() { var x = 1, y, z = 2; y = 3; return x + y + z; }\n" 245 "if (baz() == 6) r+=32;\n" // 32 246 "function Cons0() { this.x = 42; this.y = 87; }\n" 247 "if (new Cons0().x == 42) r+=64;\n" // 64 248 "if (new Cons0().y == 87) r+=128;\n" // 128 249 "function Cons2(x, y) { this.sum = x + y; }\n" 250 "if (new Cons2(3,4).sum == 7) r+=256;"; // 256 251 252 Handle<JSFunction> fun = Compile(source); 253 CHECK(!fun.is_null()); 254 bool has_pending_exception; 255 Handle<JSObject> global(Top::context()->global()); 256 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 257 CHECK(!has_pending_exception); 258 CHECK_EQ(511.0, GetGlobalProperty("r")->Number()); 259} 260 261 262TEST(UncaughtThrow) { 263 InitializeVM(); 264 v8::HandleScope scope; 265 266 const char* source = "throw 42;"; 267 Handle<JSFunction> fun = Compile(source); 268 CHECK(!fun.is_null()); 269 bool has_pending_exception; 270 Handle<JSObject> global(Top::context()->global()); 271 Handle<Object> result = 272 Execution::Call(fun, global, 0, NULL, &has_pending_exception); 273 CHECK(has_pending_exception); 274 CHECK_EQ(42.0, Top::pending_exception()->Number()); 275} 276 277 278// Tests calling a builtin function from C/C++ code, and the builtin function 279// performs GC. It creates a stack frame looks like following: 280// | C (PerformGC) | 281// | JS-to-C | 282// | JS | 283// | C-to-JS | 284TEST(C2JSFrames) { 285 InitializeVM(); 286 v8::HandleScope scope; 287 288 const char* source = "function foo(a) { gc(), print(a); }"; 289 290 Handle<JSFunction> fun0 = Compile(source); 291 CHECK(!fun0.is_null()); 292 293 // Run the generated code to populate the global object with 'foo'. 294 bool has_pending_exception; 295 Handle<JSObject> global(Top::context()->global()); 296 Execution::Call(fun0, global, 0, NULL, &has_pending_exception); 297 CHECK(!has_pending_exception); 298 299 Handle<Object> fun1 = 300 Handle<Object>( 301 Top::context()->global()->GetProperty( 302 *Factory::LookupAsciiSymbol("foo"))); 303 CHECK(fun1->IsJSFunction()); 304 305 Object** argv[1] = { 306 Handle<Object>::cast(Factory::LookupAsciiSymbol("hello")).location() 307 }; 308 Execution::Call(Handle<JSFunction>::cast(fun1), global, 1, argv, 309 &has_pending_exception); 310 CHECK(!has_pending_exception); 311} 312 313 314// Regression 236. Calling InitLineEnds on a Script with undefined 315// source resulted in crash. 316TEST(Regression236) { 317 InitializeVM(); 318 v8::HandleScope scope; 319 320 Handle<Script> script = Factory::NewScript(Factory::empty_string()); 321 script->set_source(Heap::undefined_value()); 322 CHECK_EQ(-1, GetScriptLineNumber(script, 0)); 323 CHECK_EQ(-1, GetScriptLineNumber(script, 100)); 324 CHECK_EQ(-1, GetScriptLineNumber(script, -1)); 325} 326 327 328TEST(GetScriptLineNumber) { 329 LocalContext env; 330 v8::HandleScope scope; 331 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 332 const char function_f[] = "function f() {}"; 333 const int max_rows = 1000; 334 const int buffer_size = max_rows + sizeof(function_f); 335 ScopedVector<char> buffer(buffer_size); 336 memset(buffer.start(), '\n', buffer_size - 1); 337 buffer[buffer_size - 1] = '\0'; 338 339 for (int i = 0; i < max_rows; ++i) { 340 if (i > 0) 341 buffer[i - 1] = '\n'; 342 memcpy(&buffer[i], function_f, sizeof(function_f) - 1); 343 v8::Handle<v8::String> script_body = v8::String::New(buffer.start()); 344 v8::Script::Compile(script_body, &origin)->Run(); 345 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 346 env->Global()->Get(v8::String::New("f"))); 347 CHECK_EQ(i, f->GetScriptLineNumber()); 348 } 349} 350