1// Copyright 2012 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>
30
31#include "v8.h"
32
33#include "compiler.h"
34#include "disasm.h"
35#include "disassembler.h"
36#include "execution.h"
37#include "factory.h"
38#include "platform.h"
39#include "cctest.h"
40
41using namespace v8::internal;
42
43// --- P r i n t   E x t e n s i o n ---
44
45class PrintExtension : public v8::Extension {
46 public:
47  PrintExtension() : v8::Extension("v8/print", kSource) { }
48  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
49      v8::Isolate* isolate,
50      v8::Handle<v8::String> name);
51  static void Print(const v8::FunctionCallbackInfo<v8::Value>& 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::GetNativeFunctionTemplate(
61    v8::Isolate* isolate,
62    v8::Handle<v8::String> str) {
63  return v8::FunctionTemplate::New(PrintExtension::Print);
64}
65
66
67void PrintExtension::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
68  for (int i = 0; i < args.Length(); i++) {
69    if (i != 0) printf(" ");
70    v8::HandleScope scope(args.GetIsolate());
71    v8::String::Utf8Value str(args[i]);
72    if (*str == NULL) return;
73    printf("%s", *str);
74  }
75  printf("\n");
76}
77
78
79static PrintExtension kPrintExtension;
80v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension);
81
82
83static MaybeObject* GetGlobalProperty(const char* name) {
84  Isolate* isolate = CcTest::i_isolate();
85  Handle<String> internalized_name =
86      isolate->factory()->InternalizeUtf8String(name);
87  return isolate->context()->global_object()->GetProperty(*internalized_name);
88}
89
90
91static void SetGlobalProperty(const char* name, Object* value) {
92  Isolate* isolate = CcTest::i_isolate();
93  Handle<Object> object(value, isolate);
94  Handle<String> internalized_name =
95      isolate->factory()->InternalizeUtf8String(name);
96  Handle<JSObject> global(isolate->context()->global_object());
97  Runtime::SetObjectProperty(isolate, global, internalized_name, object, NONE,
98                             kNonStrictMode);
99}
100
101
102static Handle<JSFunction> Compile(const char* source) {
103  Isolate* isolate = CcTest::i_isolate();
104  Handle<String> source_code(
105      isolate->factory()->NewStringFromUtf8(CStrVector(source)));
106  Handle<SharedFunctionInfo> shared_function =
107      Compiler::Compile(source_code,
108                        Handle<String>(),
109                        0,
110                        0,
111                        false,
112                        Handle<Context>(isolate->native_context()),
113                        NULL,
114                        NULL,
115                        Handle<String>::null(),
116                        NOT_NATIVES_CODE);
117  return isolate->factory()->NewFunctionFromSharedFunctionInfo(
118      shared_function, isolate->native_context());
119}
120
121
122static double Inc(Isolate* isolate, int x) {
123  const char* source = "result = %d + 1;";
124  EmbeddedVector<char, 512> buffer;
125  OS::SNPrintF(buffer, source, x);
126
127  Handle<JSFunction> fun = Compile(buffer.start());
128  if (fun.is_null()) return -1;
129
130  bool has_pending_exception;
131  Handle<JSObject> global(isolate->context()->global_object());
132  Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
133  CHECK(!has_pending_exception);
134  return GetGlobalProperty("result")->ToObjectChecked()->Number();
135}
136
137
138TEST(Inc) {
139  CcTest::InitializeVM();
140  v8::HandleScope scope(CcTest::isolate());
141  CHECK_EQ(4.0, Inc(CcTest::i_isolate(), 3));
142}
143
144
145static double Add(Isolate* isolate, int x, int y) {
146  Handle<JSFunction> fun = Compile("result = x + y;");
147  if (fun.is_null()) return -1;
148
149  SetGlobalProperty("x", Smi::FromInt(x));
150  SetGlobalProperty("y", Smi::FromInt(y));
151  bool has_pending_exception;
152  Handle<JSObject> global(isolate->context()->global_object());
153  Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
154  CHECK(!has_pending_exception);
155  return GetGlobalProperty("result")->ToObjectChecked()->Number();
156}
157
158
159TEST(Add) {
160  CcTest::InitializeVM();
161  v8::HandleScope scope(CcTest::isolate());
162  CHECK_EQ(5.0, Add(CcTest::i_isolate(), 2, 3));
163}
164
165
166static double Abs(Isolate* isolate, int x) {
167  Handle<JSFunction> fun = Compile("if (x < 0) result = -x; else result = x;");
168  if (fun.is_null()) return -1;
169
170  SetGlobalProperty("x", Smi::FromInt(x));
171  bool has_pending_exception;
172  Handle<JSObject> global(isolate->context()->global_object());
173  Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
174  CHECK(!has_pending_exception);
175  return GetGlobalProperty("result")->ToObjectChecked()->Number();
176}
177
178
179TEST(Abs) {
180  CcTest::InitializeVM();
181  v8::HandleScope scope(CcTest::isolate());
182  CHECK_EQ(3.0, Abs(CcTest::i_isolate(), -3));
183}
184
185
186static double Sum(Isolate* isolate, int n) {
187  Handle<JSFunction> fun =
188      Compile("s = 0; while (n > 0) { s += n; n -= 1; }; result = s;");
189  if (fun.is_null()) return -1;
190
191  SetGlobalProperty("n", Smi::FromInt(n));
192  bool has_pending_exception;
193  Handle<JSObject> global(isolate->context()->global_object());
194  Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
195  CHECK(!has_pending_exception);
196  return GetGlobalProperty("result")->ToObjectChecked()->Number();
197}
198
199
200TEST(Sum) {
201  CcTest::InitializeVM();
202  v8::HandleScope scope(CcTest::isolate());
203  CHECK_EQ(5050.0, Sum(CcTest::i_isolate(), 100));
204}
205
206
207TEST(Print) {
208  v8::HandleScope scope(CcTest::isolate());
209  v8::Local<v8::Context> context = CcTest::NewContext(PRINT_EXTENSION);
210  v8::Context::Scope context_scope(context);
211  const char* source = "for (n = 0; n < 100; ++n) print(n, 1, 2);";
212  Handle<JSFunction> fun = Compile(source);
213  if (fun.is_null()) return;
214  bool has_pending_exception;
215  Handle<JSObject> global(CcTest::i_isolate()->context()->global_object());
216  Execution::Call(
217      CcTest::i_isolate(), fun, global, 0, NULL, &has_pending_exception);
218  CHECK(!has_pending_exception);
219}
220
221
222// The following test method stems from my coding efforts today. It
223// tests all the functionality I have added to the compiler today
224TEST(Stuff) {
225  CcTest::InitializeVM();
226  v8::HandleScope scope(CcTest::isolate());
227  const char* source =
228    "r = 0;\n"
229    "a = new Object;\n"
230    "if (a == a) r+=1;\n"  // 1
231    "if (a != new Object()) r+=2;\n"  // 2
232    "a.x = 42;\n"
233    "if (a.x == 42) r+=4;\n"  // 4
234    "function foo() { var x = 87; return x; }\n"
235    "if (foo() == 87) r+=8;\n"  // 8
236    "function bar() { var x; x = 99; return x; }\n"
237    "if (bar() == 99) r+=16;\n"  // 16
238    "function baz() { var x = 1, y, z = 2; y = 3; return x + y + z; }\n"
239    "if (baz() == 6) r+=32;\n"  // 32
240    "function Cons0() { this.x = 42; this.y = 87; }\n"
241    "if (new Cons0().x == 42) r+=64;\n"  // 64
242    "if (new Cons0().y == 87) r+=128;\n"  // 128
243    "function Cons2(x, y) { this.sum = x + y; }\n"
244    "if (new Cons2(3,4).sum == 7) r+=256;";  // 256
245
246  Handle<JSFunction> fun = Compile(source);
247  CHECK(!fun.is_null());
248  bool has_pending_exception;
249  Handle<JSObject> global(CcTest::i_isolate()->context()->global_object());
250  Execution::Call(
251      CcTest::i_isolate(), fun, global, 0, NULL, &has_pending_exception);
252  CHECK(!has_pending_exception);
253  CHECK_EQ(511.0, GetGlobalProperty("r")->ToObjectChecked()->Number());
254}
255
256
257TEST(UncaughtThrow) {
258  CcTest::InitializeVM();
259  v8::HandleScope scope(CcTest::isolate());
260
261  const char* source = "throw 42;";
262  Handle<JSFunction> fun = Compile(source);
263  CHECK(!fun.is_null());
264  bool has_pending_exception;
265  Isolate* isolate = fun->GetIsolate();
266  Handle<JSObject> global(isolate->context()->global_object());
267  Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
268  CHECK(has_pending_exception);
269  CHECK_EQ(42.0, isolate->pending_exception()->ToObjectChecked()->Number());
270}
271
272
273// Tests calling a builtin function from C/C++ code, and the builtin function
274// performs GC. It creates a stack frame looks like following:
275//   | C (PerformGC) |
276//   |   JS-to-C     |
277//   |      JS       |
278//   |   C-to-JS     |
279TEST(C2JSFrames) {
280  v8::HandleScope scope(CcTest::isolate());
281  v8::Local<v8::Context> context =
282    CcTest::NewContext(PRINT_EXTENSION | GC_EXTENSION);
283  v8::Context::Scope context_scope(context);
284
285  const char* source = "function foo(a) { gc(), print(a); }";
286
287  Handle<JSFunction> fun0 = Compile(source);
288  CHECK(!fun0.is_null());
289  Isolate* isolate = fun0->GetIsolate();
290
291  // Run the generated code to populate the global object with 'foo'.
292  bool has_pending_exception;
293  Handle<JSObject> global(isolate->context()->global_object());
294  Execution::Call(
295      isolate, fun0, global, 0, NULL, &has_pending_exception);
296  CHECK(!has_pending_exception);
297
298  Object* foo_string = isolate->factory()->InternalizeOneByteString(
299      STATIC_ASCII_VECTOR("foo"))->ToObjectChecked();
300  MaybeObject* fun1_object = isolate->context()->global_object()->
301      GetProperty(String::cast(foo_string));
302  Handle<Object> fun1(fun1_object->ToObjectChecked(), isolate);
303  CHECK(fun1->IsJSFunction());
304
305  Handle<Object> argv[] = { isolate->factory()->InternalizeOneByteString(
306      STATIC_ASCII_VECTOR("hello")) };
307  Execution::Call(isolate,
308                  Handle<JSFunction>::cast(fun1),
309                  global,
310                  ARRAY_SIZE(argv),
311                  argv,
312                  &has_pending_exception);
313  CHECK(!has_pending_exception);
314}
315
316
317// Regression 236. Calling InitLineEnds on a Script with undefined
318// source resulted in crash.
319TEST(Regression236) {
320  CcTest::InitializeVM();
321  Isolate* isolate = CcTest::i_isolate();
322  Factory* factory = isolate->factory();
323  v8::HandleScope scope(CcTest::isolate());
324
325  Handle<Script> script = factory->NewScript(factory->empty_string());
326  script->set_source(CcTest::heap()->undefined_value());
327  CHECK_EQ(-1, GetScriptLineNumber(script, 0));
328  CHECK_EQ(-1, GetScriptLineNumber(script, 100));
329  CHECK_EQ(-1, GetScriptLineNumber(script, -1));
330}
331
332
333TEST(GetScriptLineNumber) {
334  LocalContext context;
335  v8::HandleScope scope(CcTest::isolate());
336  v8::ScriptOrigin origin =
337      v8::ScriptOrigin(v8::String::NewFromUtf8(CcTest::isolate(), "test"));
338  const char function_f[] = "function f() {}";
339  const int max_rows = 1000;
340  const int buffer_size = max_rows + sizeof(function_f);
341  ScopedVector<char> buffer(buffer_size);
342  memset(buffer.start(), '\n', buffer_size - 1);
343  buffer[buffer_size - 1] = '\0';
344
345  for (int i = 0; i < max_rows; ++i) {
346    if (i > 0)
347      buffer[i - 1] = '\n';
348    OS::MemCopy(&buffer[i], function_f, sizeof(function_f) - 1);
349    v8::Handle<v8::String> script_body =
350        v8::String::NewFromUtf8(CcTest::isolate(), buffer.start());
351    v8::Script::Compile(script_body, &origin)->Run();
352    v8::Local<v8::Function> f =
353        v8::Local<v8::Function>::Cast(context->Global()->Get(
354            v8::String::NewFromUtf8(CcTest::isolate(), "f")));
355    CHECK_EQ(i, f->GetScriptLineNumber());
356  }
357}
358
359
360// Test that optimized code for different closures is actually shared
361// immediately by the FastNewClosureStub when run in the same context.
362TEST(OptimizedCodeSharing) {
363  // Skip test if --cache-optimized-code is not activated by default because
364  // FastNewClosureStub that is baked into the snapshot is incorrect.
365  if (!FLAG_cache_optimized_code) return;
366  FLAG_stress_compaction = false;
367  FLAG_allow_natives_syntax = true;
368  CcTest::InitializeVM();
369  v8::HandleScope scope(CcTest::isolate());
370  for (int i = 0; i < 10; i++) {
371    LocalContext env;
372    env->Global()->Set(v8::String::NewFromUtf8(CcTest::isolate(), "x"),
373                       v8::Integer::New(i));
374    CompileRun("function MakeClosure() {"
375               "  return function() { return x; };"
376               "}"
377               "var closure0 = MakeClosure();"
378               "%DebugPrint(closure0());"
379               "%OptimizeFunctionOnNextCall(closure0);"
380               "%DebugPrint(closure0());"
381               "var closure1 = MakeClosure();"
382               "var closure2 = MakeClosure();");
383    Handle<JSFunction> fun1 = v8::Utils::OpenHandle(
384        *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure1"))));
385    Handle<JSFunction> fun2 = v8::Utils::OpenHandle(
386        *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure2"))));
387    CHECK(fun1->IsOptimized()
388          || !CcTest::i_isolate()->use_crankshaft() || !fun1->IsOptimizable());
389    CHECK(fun2->IsOptimized()
390          || !CcTest::i_isolate()->use_crankshaft() || !fun2->IsOptimizable());
391    CHECK_EQ(fun1->code(), fun2->code());
392  }
393}
394
395
396#ifdef ENABLE_DISASSEMBLER
397static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj,
398                                 const char* property_name) {
399  v8::Local<v8::Function> fun =
400      v8::Local<v8::Function>::Cast(obj->Get(v8_str(property_name)));
401  return v8::Utils::OpenHandle(*fun);
402}
403
404
405static void CheckCodeForUnsafeLiteral(Handle<JSFunction> f) {
406  // Create a disassembler with default name lookup.
407  disasm::NameConverter name_converter;
408  disasm::Disassembler d(name_converter);
409
410  if (f->code()->kind() == Code::FUNCTION) {
411    Address pc = f->code()->instruction_start();
412    int decode_size =
413        Min(f->code()->instruction_size(),
414            static_cast<int>(f->code()->back_edge_table_offset()));
415    Address end = pc + decode_size;
416
417    v8::internal::EmbeddedVector<char, 128> decode_buffer;
418    v8::internal::EmbeddedVector<char, 128> smi_hex_buffer;
419    Smi* smi = Smi::FromInt(12345678);
420    OS::SNPrintF(smi_hex_buffer, "0x%lx", reinterpret_cast<intptr_t>(smi));
421    while (pc < end) {
422      int num_const = d.ConstantPoolSizeAt(pc);
423      if (num_const >= 0) {
424        pc += (num_const + 1) * kPointerSize;
425      } else {
426        pc += d.InstructionDecode(decode_buffer, pc);
427        CHECK(strstr(decode_buffer.start(), smi_hex_buffer.start()) == NULL);
428      }
429    }
430  }
431}
432
433
434TEST(SplitConstantsInFullCompiler) {
435  LocalContext context;
436  v8::HandleScope scope(CcTest::isolate());
437
438  CompileRun("function f() { a = 12345678 }; f();");
439  CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
440  CompileRun("function f(x) { a = 12345678 + x}; f(1);");
441  CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
442  CompileRun("function f(x) { var arguments = 1; x += 12345678}; f(1);");
443  CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
444  CompileRun("function f(x) { var arguments = 1; x = 12345678}; f(1);");
445  CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
446}
447#endif
448