Split more runtime functions into seperate files.
R=bmeurer@chromium.org BUG= Review URL: https://codereview.chromium.org/598913004 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24274 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
6312804eb3
commit
b0fdeb1b2a
6
BUILD.gn
6
BUILD.gn
@ -824,10 +824,16 @@ source_set("v8_base") {
|
||||
"src/rewriter.h",
|
||||
"src/runtime-profiler.cc",
|
||||
"src/runtime-profiler.h",
|
||||
"src/runtime/runtime-collections.cc",
|
||||
"src/runtime/runtime-compiler.cc",
|
||||
"src/runtime/runtime-i18n.cc",
|
||||
"src/runtime/runtime-json.cc",
|
||||
"src/runtime/runtime-maths.cc",
|
||||
"src/runtime/runtime-numbers.cc",
|
||||
"src/runtime/runtime-regexp.cc",
|
||||
"src/runtime/runtime-strings.cc",
|
||||
"src/runtime/runtime-test.cc",
|
||||
"src/runtime/runtime-typedarray.cc",
|
||||
"src/runtime/runtime-uri.cc",
|
||||
"src/runtime/runtime-utils.h",
|
||||
"src/runtime/runtime.cc",
|
||||
|
347
src/runtime/runtime-collections.cc
Normal file
347
src/runtime/runtime-collections.cc
Normal file
@ -0,0 +1,347 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
|
||||
Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
|
||||
holder->set_table(*table);
|
||||
return *holder;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetAdd) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
|
||||
table = OrderedHashSet::Add(table, key);
|
||||
holder->set_table(*table);
|
||||
return *holder;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetHas) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
|
||||
return isolate->heap()->ToBoolean(table->Contains(key));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetDelete) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
|
||||
bool was_present = false;
|
||||
table = OrderedHashSet::Remove(table, key, &was_present);
|
||||
holder->set_table(*table);
|
||||
return isolate->heap()->ToBoolean(was_present);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetClear) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
|
||||
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
|
||||
table = OrderedHashSet::Clear(table);
|
||||
holder->set_table(*table);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetGetSize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
|
||||
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
|
||||
return Smi::FromInt(table->NumberOfElements());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSSet, set, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(kind, 2)
|
||||
RUNTIME_ASSERT(kind == JSSetIterator::kKindValues ||
|
||||
kind == JSSetIterator::kKindEntries);
|
||||
Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
|
||||
holder->set_table(*table);
|
||||
holder->set_index(Smi::FromInt(0));
|
||||
holder->set_kind(Smi::FromInt(kind));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_CHECKED(JSSetIterator, holder, 0);
|
||||
CONVERT_ARG_CHECKED(JSArray, value_array, 1);
|
||||
return holder->Next(value_array);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
|
||||
holder->set_table(*table);
|
||||
return *holder;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapGet) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
|
||||
Handle<Object> lookup(table->Lookup(key), isolate);
|
||||
return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapHas) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
|
||||
Handle<Object> lookup(table->Lookup(key), isolate);
|
||||
return isolate->heap()->ToBoolean(!lookup->IsTheHole());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapDelete) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
|
||||
bool was_present = false;
|
||||
Handle<OrderedHashMap> new_table =
|
||||
OrderedHashMap::Remove(table, key, &was_present);
|
||||
holder->set_table(*new_table);
|
||||
return isolate->heap()->ToBoolean(was_present);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapClear) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
|
||||
table = OrderedHashMap::Clear(table);
|
||||
holder->set_table(*table);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapSet) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
|
||||
Handle<OrderedHashMap> new_table = OrderedHashMap::Put(table, key, value);
|
||||
holder->set_table(*new_table);
|
||||
return *holder;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapGetSize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
|
||||
return Smi::FromInt(table->NumberOfElements());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapIteratorInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSMap, map, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(kind, 2)
|
||||
RUNTIME_ASSERT(kind == JSMapIterator::kKindKeys ||
|
||||
kind == JSMapIterator::kKindValues ||
|
||||
kind == JSMapIterator::kKindEntries);
|
||||
Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()));
|
||||
holder->set_table(*table);
|
||||
holder->set_index(Smi::FromInt(0));
|
||||
holder->set_kind(Smi::FromInt(kind));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
|
||||
Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
|
||||
Handle<FixedArray> entries =
|
||||
isolate->factory()->NewFixedArray(table->NumberOfElements() * 2);
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
int number_of_non_hole_elements = 0;
|
||||
for (int i = 0; i < table->Capacity(); i++) {
|
||||
Handle<Object> key(table->KeyAt(i), isolate);
|
||||
if (table->IsKey(*key)) {
|
||||
entries->set(number_of_non_hole_elements++, *key);
|
||||
Object* value = table->Lookup(key);
|
||||
entries->set(number_of_non_hole_elements++, value);
|
||||
}
|
||||
}
|
||||
DCHECK_EQ(table->NumberOfElements() * 2, number_of_non_hole_elements);
|
||||
}
|
||||
return *isolate->factory()->NewJSArrayWithElements(entries);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MapIteratorNext) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_CHECKED(JSMapIterator, holder, 0);
|
||||
CONVERT_ARG_CHECKED(JSArray, value_array, 1);
|
||||
return holder->Next(value_array);
|
||||
}
|
||||
|
||||
|
||||
static Handle<JSWeakCollection> WeakCollectionInitialize(
|
||||
Isolate* isolate, Handle<JSWeakCollection> weak_collection) {
|
||||
DCHECK(weak_collection->map()->inobject_properties() == 0);
|
||||
Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 0);
|
||||
weak_collection->set_table(*table);
|
||||
return weak_collection;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
|
||||
return *WeakCollectionInitialize(isolate, weak_collection);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WeakCollectionGet) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
|
||||
Handle<ObjectHashTable> table(
|
||||
ObjectHashTable::cast(weak_collection->table()));
|
||||
RUNTIME_ASSERT(table->IsKey(*key));
|
||||
Handle<Object> lookup(table->Lookup(key), isolate);
|
||||
return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WeakCollectionHas) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
|
||||
Handle<ObjectHashTable> table(
|
||||
ObjectHashTable::cast(weak_collection->table()));
|
||||
RUNTIME_ASSERT(table->IsKey(*key));
|
||||
Handle<Object> lookup(table->Lookup(key), isolate);
|
||||
return isolate->heap()->ToBoolean(!lookup->IsTheHole());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
|
||||
Handle<ObjectHashTable> table(
|
||||
ObjectHashTable::cast(weak_collection->table()));
|
||||
RUNTIME_ASSERT(table->IsKey(*key));
|
||||
bool was_present = false;
|
||||
Handle<ObjectHashTable> new_table =
|
||||
ObjectHashTable::Remove(table, key, &was_present);
|
||||
weak_collection->set_table(*new_table);
|
||||
return isolate->heap()->ToBoolean(was_present);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WeakCollectionSet) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
Handle<ObjectHashTable> table(
|
||||
ObjectHashTable::cast(weak_collection->table()));
|
||||
RUNTIME_ASSERT(table->IsKey(*key));
|
||||
Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
|
||||
weak_collection->set_table(*new_table);
|
||||
return *weak_collection;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetWeakSetValues) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
|
||||
Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
|
||||
Handle<FixedArray> values =
|
||||
isolate->factory()->NewFixedArray(table->NumberOfElements());
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
int number_of_non_hole_elements = 0;
|
||||
for (int i = 0; i < table->Capacity(); i++) {
|
||||
Handle<Object> key(table->KeyAt(i), isolate);
|
||||
if (table->IsKey(*key)) {
|
||||
values->set(number_of_non_hole_elements++, *key);
|
||||
}
|
||||
}
|
||||
DCHECK_EQ(table->NumberOfElements(), number_of_non_hole_elements);
|
||||
}
|
||||
return *isolate->factory()->NewJSArrayWithElements(values);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ObservationWeakMapCreate) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
// TODO(adamk): Currently this runtime function is only called three times per
|
||||
// isolate. If it's called more often, the map should be moved into the
|
||||
// strong root list.
|
||||
Handle<Map> map =
|
||||
isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
|
||||
Handle<JSWeakMap> weakmap =
|
||||
Handle<JSWeakMap>::cast(isolate->factory()->NewJSObjectFromMap(map));
|
||||
return *WeakCollectionInitialize(isolate, weakmap);
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
441
src/runtime/runtime-compiler.cc
Normal file
441
src/runtime/runtime-compiler.cc
Normal file
@ -0,0 +1,441 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/compiler.h"
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/frames.h"
|
||||
#include "src/full-codegen.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
#include "src/v8threads.h"
|
||||
#include "src/vm-state.h"
|
||||
#include "src/vm-state-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileLazy) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
#ifdef DEBUG
|
||||
if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
|
||||
PrintF("[unoptimized: ");
|
||||
function->PrintName();
|
||||
PrintF("]\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compile the target function.
|
||||
DCHECK(function->shared()->allows_lazy_compilation());
|
||||
|
||||
Handle<Code> code;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, code,
|
||||
Compiler::GetLazyCode(function));
|
||||
DCHECK(code->kind() == Code::FUNCTION ||
|
||||
code->kind() == Code::OPTIMIZED_FUNCTION);
|
||||
function->ReplaceCode(*code);
|
||||
return *code;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileOptimized) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(concurrent, 1);
|
||||
|
||||
Handle<Code> unoptimized(function->shared()->code());
|
||||
if (!isolate->use_crankshaft() ||
|
||||
function->shared()->optimization_disabled() ||
|
||||
isolate->DebuggerHasBreakPoints()) {
|
||||
// If the function is not optimizable or debugger is active continue
|
||||
// using the code from the full compiler.
|
||||
if (FLAG_trace_opt) {
|
||||
PrintF("[failed to optimize ");
|
||||
function->PrintName();
|
||||
PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
|
||||
function->shared()->optimization_disabled() ? "F" : "T",
|
||||
isolate->DebuggerHasBreakPoints() ? "T" : "F");
|
||||
}
|
||||
function->ReplaceCode(*unoptimized);
|
||||
return function->code();
|
||||
}
|
||||
|
||||
Compiler::ConcurrencyMode mode =
|
||||
concurrent ? Compiler::CONCURRENT : Compiler::NOT_CONCURRENT;
|
||||
Handle<Code> code;
|
||||
if (Compiler::GetOptimizedCode(function, unoptimized, mode).ToHandle(&code)) {
|
||||
function->ReplaceCode(*code);
|
||||
} else {
|
||||
function->ReplaceCode(function->shared()->code());
|
||||
}
|
||||
|
||||
DCHECK(function->code()->kind() == Code::FUNCTION ||
|
||||
function->code()->kind() == Code::OPTIMIZED_FUNCTION ||
|
||||
function->IsInOptimizationQueue());
|
||||
return function->code();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
|
||||
DCHECK(AllowHeapAllocation::IsAllowed());
|
||||
delete deoptimizer;
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
class ActivationsFinder : public ThreadVisitor {
|
||||
public:
|
||||
Code* code_;
|
||||
bool has_code_activations_;
|
||||
|
||||
explicit ActivationsFinder(Code* code)
|
||||
: code_(code), has_code_activations_(false) {}
|
||||
|
||||
void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
|
||||
JavaScriptFrameIterator it(isolate, top);
|
||||
VisitFrames(&it);
|
||||
}
|
||||
|
||||
void VisitFrames(JavaScriptFrameIterator* it) {
|
||||
for (; !it->done(); it->Advance()) {
|
||||
JavaScriptFrame* frame = it->frame();
|
||||
if (code_->contains(frame->pc())) has_code_activations_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_SMI_ARG_CHECKED(type_arg, 0);
|
||||
Deoptimizer::BailoutType type =
|
||||
static_cast<Deoptimizer::BailoutType>(type_arg);
|
||||
Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
|
||||
DCHECK(AllowHeapAllocation::IsAllowed());
|
||||
|
||||
Handle<JSFunction> function = deoptimizer->function();
|
||||
Handle<Code> optimized_code = deoptimizer->compiled_code();
|
||||
|
||||
DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
|
||||
DCHECK(type == deoptimizer->bailout_type());
|
||||
|
||||
// Make sure to materialize objects before causing any allocation.
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
deoptimizer->MaterializeHeapObjects(&it);
|
||||
delete deoptimizer;
|
||||
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
RUNTIME_ASSERT(frame->function()->IsJSFunction());
|
||||
DCHECK(frame->function() == *function);
|
||||
|
||||
// Avoid doing too much work when running with --always-opt and keep
|
||||
// the optimized code around.
|
||||
if (FLAG_always_opt || type == Deoptimizer::LAZY) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
// Search for other activations of the same function and code.
|
||||
ActivationsFinder activations_finder(*optimized_code);
|
||||
activations_finder.VisitFrames(&it);
|
||||
isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
|
||||
|
||||
if (!activations_finder.has_code_activations_) {
|
||||
if (function->code() == *optimized_code) {
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF("[removing optimized code for: ");
|
||||
function->PrintName();
|
||||
PrintF("]\n");
|
||||
}
|
||||
function->ReplaceCode(function->shared()->code());
|
||||
// Evict optimized code for this function from the cache so that it
|
||||
// doesn't get used for new closures.
|
||||
function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
|
||||
"notify deoptimized");
|
||||
}
|
||||
} else {
|
||||
// TODO(titzer): we should probably do DeoptimizeCodeList(code)
|
||||
// unconditionally if the code is not already marked for deoptimization.
|
||||
// If there is an index by shared function info, all the better.
|
||||
Deoptimizer::DeoptimizeFunction(*function);
|
||||
}
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
static bool IsSuitableForOnStackReplacement(Isolate* isolate,
|
||||
Handle<JSFunction> function,
|
||||
Handle<Code> current_code) {
|
||||
// Keep track of whether we've succeeded in optimizing.
|
||||
if (!isolate->use_crankshaft() || !current_code->optimizable()) return false;
|
||||
// If we are trying to do OSR when there are already optimized
|
||||
// activations of the function, it means (a) the function is directly or
|
||||
// indirectly recursive and (b) an optimized invocation has been
|
||||
// deoptimized so that we are currently in an unoptimized activation.
|
||||
// Check for optimized activations of this function.
|
||||
for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
if (frame->is_optimized() && frame->function() == *function) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
Handle<Code> caller_code(function->shared()->code());
|
||||
|
||||
// We're not prepared to handle a function with arguments object.
|
||||
DCHECK(!function->shared()->uses_arguments());
|
||||
|
||||
RUNTIME_ASSERT(FLAG_use_osr);
|
||||
|
||||
// Passing the PC in the javascript frame from the caller directly is
|
||||
// not GC safe, so we walk the stack to get it.
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
if (!caller_code->contains(frame->pc())) {
|
||||
// Code on the stack may not be the code object referenced by the shared
|
||||
// function info. It may have been replaced to include deoptimization data.
|
||||
caller_code = Handle<Code>(frame->LookupCode());
|
||||
}
|
||||
|
||||
uint32_t pc_offset =
|
||||
static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
|
||||
|
||||
#ifdef DEBUG
|
||||
DCHECK_EQ(frame->function(), *function);
|
||||
DCHECK_EQ(frame->LookupCode(), *caller_code);
|
||||
DCHECK(caller_code->contains(frame->pc()));
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset);
|
||||
DCHECK(!ast_id.IsNone());
|
||||
|
||||
Compiler::ConcurrencyMode mode =
|
||||
isolate->concurrent_osr_enabled() &&
|
||||
(function->shared()->ast_node_count() > 512)
|
||||
? Compiler::CONCURRENT
|
||||
: Compiler::NOT_CONCURRENT;
|
||||
Handle<Code> result = Handle<Code>::null();
|
||||
|
||||
OptimizedCompileJob* job = NULL;
|
||||
if (mode == Compiler::CONCURRENT) {
|
||||
// Gate the OSR entry with a stack check.
|
||||
BackEdgeTable::AddStackCheck(caller_code, pc_offset);
|
||||
// Poll already queued compilation jobs.
|
||||
OptimizingCompilerThread* thread = isolate->optimizing_compiler_thread();
|
||||
if (thread->IsQueuedForOSR(function, ast_id)) {
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[OSR - Still waiting for queued: ");
|
||||
function->PrintName();
|
||||
PrintF(" at AST id %d]\n", ast_id.ToInt());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
job = thread->FindReadyOSRCandidate(function, ast_id);
|
||||
}
|
||||
|
||||
if (job != NULL) {
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[OSR - Found ready: ");
|
||||
function->PrintName();
|
||||
PrintF(" at AST id %d]\n", ast_id.ToInt());
|
||||
}
|
||||
result = Compiler::GetConcurrentlyOptimizedCode(job);
|
||||
} else if (IsSuitableForOnStackReplacement(isolate, function, caller_code)) {
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[OSR - Compiling: ");
|
||||
function->PrintName();
|
||||
PrintF(" at AST id %d]\n", ast_id.ToInt());
|
||||
}
|
||||
MaybeHandle<Code> maybe_result =
|
||||
Compiler::GetOptimizedCode(function, caller_code, mode, ast_id);
|
||||
if (maybe_result.ToHandle(&result) &&
|
||||
result.is_identical_to(isolate->builtins()->InOptimizationQueue())) {
|
||||
// Optimization is queued. Return to check later.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Revert the patched back edge table, regardless of whether OSR succeeds.
|
||||
BackEdgeTable::Revert(isolate, *caller_code);
|
||||
|
||||
// Check whether we ended up with usable optimized code.
|
||||
if (!result.is_null() && result->kind() == Code::OPTIMIZED_FUNCTION) {
|
||||
DeoptimizationInputData* data =
|
||||
DeoptimizationInputData::cast(result->deoptimization_data());
|
||||
|
||||
if (data->OsrPcOffset()->value() >= 0) {
|
||||
DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
|
||||
ast_id.ToInt(), data->OsrPcOffset()->value());
|
||||
}
|
||||
// TODO(titzer): this is a massive hack to make the deopt counts
|
||||
// match. Fix heuristics for reenabling optimizations!
|
||||
function->shared()->increment_deopt_count();
|
||||
|
||||
// TODO(titzer): Do not install code into the function.
|
||||
function->ReplaceCode(*result);
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed.
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF("[OSR - Failed: ");
|
||||
function->PrintName();
|
||||
PrintF(" at AST id %d]\n", ast_id.ToInt());
|
||||
}
|
||||
|
||||
if (!function->IsOptimized()) {
|
||||
function->ReplaceCode(function->shared()->code());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
|
||||
// First check if this is a real stack overflow.
|
||||
StackLimitCheck check(isolate);
|
||||
if (check.JsHasOverflowed()) {
|
||||
SealHandleScope shs(isolate);
|
||||
return isolate->StackOverflow();
|
||||
}
|
||||
|
||||
isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
|
||||
return (function->IsOptimized()) ? function->code()
|
||||
: function->shared()->code();
|
||||
}
|
||||
|
||||
|
||||
bool CodeGenerationFromStringsAllowed(Isolate* isolate,
|
||||
Handle<Context> context) {
|
||||
DCHECK(context->allow_code_gen_from_strings()->IsFalse());
|
||||
// Check with callback if set.
|
||||
AllowCodeGenerationFromStringsCallback callback =
|
||||
isolate->allow_code_gen_callback();
|
||||
if (callback == NULL) {
|
||||
// No callback set and code generation disallowed.
|
||||
return false;
|
||||
} else {
|
||||
// Callback set. Let it decide if code generation is allowed.
|
||||
VMState<EXTERNAL> state(isolate);
|
||||
return callback(v8::Utils::ToLocal(context));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileString) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(function_literal_only, 1);
|
||||
|
||||
// Extract native context.
|
||||
Handle<Context> context(isolate->native_context());
|
||||
|
||||
// Check if native context allows code generation from
|
||||
// strings. Throw an exception if it doesn't.
|
||||
if (context->allow_code_gen_from_strings()->IsFalse() &&
|
||||
!CodeGenerationFromStringsAllowed(isolate, context)) {
|
||||
Handle<Object> error_message =
|
||||
context->ErrorMessageForCodeGenerationFromStrings();
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewEvalError("code_gen_from_strings",
|
||||
HandleVector<Object>(&error_message, 1)));
|
||||
}
|
||||
|
||||
// Compile source string in the native context.
|
||||
ParseRestriction restriction = function_literal_only
|
||||
? ONLY_SINGLE_FUNCTION_LITERAL
|
||||
: NO_PARSE_RESTRICTION;
|
||||
Handle<JSFunction> fun;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, fun,
|
||||
Compiler::GetFunctionFromEval(source, context, SLOPPY, restriction,
|
||||
RelocInfo::kNoPosition));
|
||||
return *fun;
|
||||
}
|
||||
|
||||
|
||||
static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
|
||||
Handle<Object> receiver,
|
||||
StrictMode strict_mode,
|
||||
int scope_position) {
|
||||
Handle<Context> context = Handle<Context>(isolate->context());
|
||||
Handle<Context> native_context = Handle<Context>(context->native_context());
|
||||
|
||||
// Check if native context allows code generation from
|
||||
// strings. Throw an exception if it doesn't.
|
||||
if (native_context->allow_code_gen_from_strings()->IsFalse() &&
|
||||
!CodeGenerationFromStringsAllowed(isolate, native_context)) {
|
||||
Handle<Object> error_message =
|
||||
native_context->ErrorMessageForCodeGenerationFromStrings();
|
||||
Handle<Object> error;
|
||||
MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
|
||||
"code_gen_from_strings", HandleVector<Object>(&error_message, 1));
|
||||
if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
|
||||
return MakePair(isolate->heap()->exception(), NULL);
|
||||
}
|
||||
|
||||
// Deal with a normal eval call with a string argument. Compile it
|
||||
// and return the compiled function bound in the local context.
|
||||
static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
|
||||
Handle<JSFunction> compiled;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, compiled,
|
||||
Compiler::GetFunctionFromEval(source, context, strict_mode, restriction,
|
||||
scope_position),
|
||||
MakePair(isolate->heap()->exception(), NULL));
|
||||
return MakePair(*compiled, *receiver);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ResolvePossiblyDirectEval) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 5);
|
||||
|
||||
Handle<Object> callee = args.at<Object>(0);
|
||||
|
||||
// If "eval" didn't refer to the original GlobalEval, it's not a
|
||||
// direct call to eval.
|
||||
// (And even if it is, but the first argument isn't a string, just let
|
||||
// execution default to an indirect call to eval, which will also return
|
||||
// the first argument without doing anything).
|
||||
if (*callee != isolate->native_context()->global_eval_fun() ||
|
||||
!args[1]->IsString()) {
|
||||
return MakePair(*callee, isolate->heap()->undefined_value());
|
||||
}
|
||||
|
||||
DCHECK(args[3]->IsSmi());
|
||||
DCHECK(args.smi_at(3) == SLOPPY || args.smi_at(3) == STRICT);
|
||||
StrictMode strict_mode = static_cast<StrictMode>(args.smi_at(3));
|
||||
DCHECK(args[4]->IsSmi());
|
||||
return CompileGlobalEval(isolate, args.at<String>(1), args.at<Object>(2),
|
||||
strict_mode, args.smi_at(4));
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
247
src/runtime/runtime-maths.cc
Normal file
247
src/runtime/runtime-maths.cc
Normal file
@ -0,0 +1,247 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/assembler.h"
|
||||
#include "src/codegen.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
#include "third_party/fdlibm/fdlibm.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define RUNTIME_UNARY_MATH(Name, name) \
|
||||
RUNTIME_FUNCTION(Runtime_Math##Name) { \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK(args.length() == 1); \
|
||||
isolate->counters()->math_##name()->Increment(); \
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0); \
|
||||
return *isolate->factory()->NewHeapNumber(std::name(x)); \
|
||||
}
|
||||
|
||||
RUNTIME_UNARY_MATH(Acos, acos)
|
||||
RUNTIME_UNARY_MATH(Asin, asin)
|
||||
RUNTIME_UNARY_MATH(Atan, atan)
|
||||
RUNTIME_UNARY_MATH(LogRT, log)
|
||||
#undef RUNTIME_UNARY_MATH
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DoubleHi) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
uint64_t integer = double_to_uint64(x);
|
||||
integer = (integer >> 32) & 0xFFFFFFFFu;
|
||||
return *isolate->factory()->NewNumber(static_cast<int32_t>(integer));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DoubleLo) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return *isolate->factory()->NewNumber(
|
||||
static_cast<int32_t>(double_to_uint64(x) & 0xFFFFFFFFu));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ConstructDouble) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_NUMBER_CHECKED(uint32_t, hi, Uint32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(uint32_t, lo, Uint32, args[1]);
|
||||
uint64_t result = (static_cast<uint64_t>(hi) << 32) | lo;
|
||||
return *isolate->factory()->NewNumber(uint64_to_double(result));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_RemPiO2) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
Factory* factory = isolate->factory();
|
||||
double y[2];
|
||||
int n = fdlibm::rempio2(x, y);
|
||||
Handle<FixedArray> array = factory->NewFixedArray(3);
|
||||
Handle<HeapNumber> y0 = factory->NewHeapNumber(y[0]);
|
||||
Handle<HeapNumber> y1 = factory->NewHeapNumber(y[1]);
|
||||
array->set(0, Smi::FromInt(n));
|
||||
array->set(1, *y0);
|
||||
array->set(2, *y1);
|
||||
return *factory->NewJSArrayWithElements(array);
|
||||
}
|
||||
|
||||
|
||||
static const double kPiDividedBy4 = 0.78539816339744830962;
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MathAtan2) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
isolate->counters()->math_atan2()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
double result;
|
||||
if (std::isinf(x) && std::isinf(y)) {
|
||||
// Make sure that the result in case of two infinite arguments
|
||||
// is a multiple of Pi / 4. The sign of the result is determined
|
||||
// by the first argument (x) and the sign of the second argument
|
||||
// determines the multiplier: one or three.
|
||||
int multiplier = (x < 0) ? -1 : 1;
|
||||
if (y < 0) multiplier *= 3;
|
||||
result = multiplier * kPiDividedBy4;
|
||||
} else {
|
||||
result = std::atan2(x, y);
|
||||
}
|
||||
return *isolate->factory()->NewNumber(result);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MathExpRT) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
isolate->counters()->math_exp()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
lazily_initialize_fast_exp();
|
||||
return *isolate->factory()->NewNumber(fast_exp(x));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MathFloorRT) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
isolate->counters()->math_floor()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return *isolate->factory()->NewNumber(Floor(x));
|
||||
}
|
||||
|
||||
|
||||
// Slow version of Math.pow. We check for fast paths for special cases.
|
||||
// Used if VFP3 is not available.
|
||||
RUNTIME_FUNCTION(Runtime_MathPowSlow) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
isolate->counters()->math_pow()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
|
||||
// If the second argument is a smi, it is much faster to call the
|
||||
// custom powi() function than the generic pow().
|
||||
if (args[1]->IsSmi()) {
|
||||
int y = args.smi_at(1);
|
||||
return *isolate->factory()->NewNumber(power_double_int(x, y));
|
||||
}
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
double result = power_helper(x, y);
|
||||
if (std::isnan(result)) return isolate->heap()->nan_value();
|
||||
return *isolate->factory()->NewNumber(result);
|
||||
}
|
||||
|
||||
|
||||
// Fast version of Math.pow if we know that y is not an integer and y is not
|
||||
// -0.5 or 0.5. Used as slow case from full codegen.
|
||||
RUNTIME_FUNCTION(Runtime_MathPowRT) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
isolate->counters()->math_pow()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
if (y == 0) {
|
||||
return Smi::FromInt(1);
|
||||
} else {
|
||||
double result = power_double_double(x, y);
|
||||
if (std::isnan(result)) return isolate->heap()->nan_value();
|
||||
return *isolate->factory()->NewNumber(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_RoundNumber) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(input, 0);
|
||||
isolate->counters()->math_round()->Increment();
|
||||
|
||||
if (!input->IsHeapNumber()) {
|
||||
DCHECK(input->IsSmi());
|
||||
return *input;
|
||||
}
|
||||
|
||||
Handle<HeapNumber> number = Handle<HeapNumber>::cast(input);
|
||||
|
||||
double value = number->value();
|
||||
int exponent = number->get_exponent();
|
||||
int sign = number->get_sign();
|
||||
|
||||
if (exponent < -1) {
|
||||
// Number in range ]-0.5..0.5[. These always round to +/-zero.
|
||||
if (sign) return isolate->heap()->minus_zero_value();
|
||||
return Smi::FromInt(0);
|
||||
}
|
||||
|
||||
// We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and
|
||||
// should be rounded to 2^30, which is not smi (for 31-bit smis, similar
|
||||
// argument holds for 32-bit smis).
|
||||
if (!sign && exponent < kSmiValueSize - 2) {
|
||||
return Smi::FromInt(static_cast<int>(value + 0.5));
|
||||
}
|
||||
|
||||
// If the magnitude is big enough, there's no place for fraction part. If we
|
||||
// try to add 0.5 to this number, 1.0 will be added instead.
|
||||
if (exponent >= 52) {
|
||||
return *number;
|
||||
}
|
||||
|
||||
if (sign && value >= -0.5) return isolate->heap()->minus_zero_value();
|
||||
|
||||
// Do not call NumberFromDouble() to avoid extra checks.
|
||||
return *isolate->factory()->NewNumber(Floor(value + 0.5));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MathSqrtRT) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
isolate->counters()->math_sqrt()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return *isolate->factory()->NewNumber(fast_sqrt(x));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MathFround) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
float xf = DoubleToFloat32(x);
|
||||
return *isolate->factory()->NewNumber(xf);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(RuntimeReference_MathPow) {
|
||||
SealHandleScope shs(isolate);
|
||||
return __RT_impl_Runtime_MathPowSlow(args, isolate);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(RuntimeReference_IsMinusZero) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(Object, obj, 0);
|
||||
if (!obj->IsHeapNumber()) return isolate->heap()->false_value();
|
||||
HeapNumber* number = HeapNumber::cast(obj);
|
||||
return isolate->heap()->ToBoolean(IsMinusZero(number->value()));
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
565
src/runtime/runtime-numbers.cc
Normal file
565
src/runtime/runtime-numbers.cc
Normal file
@ -0,0 +1,565 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/codegen.h"
|
||||
#include "src/misc-intrinsics.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
|
||||
|
||||
#ifndef _STLP_VENDOR_CSTD
|
||||
// STLPort doesn't import fpclassify and isless into the std namespace.
|
||||
using std::fpclassify;
|
||||
using std::isless;
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToRadixString) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_SMI_ARG_CHECKED(radix, 1);
|
||||
RUNTIME_ASSERT(2 <= radix && radix <= 36);
|
||||
|
||||
// Fast case where the result is a one character string.
|
||||
if (args[0]->IsSmi()) {
|
||||
int value = args.smi_at(0);
|
||||
if (value >= 0 && value < radix) {
|
||||
// Character array used for conversion.
|
||||
static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
return *isolate->factory()->LookupSingleCharacterStringFromCode(
|
||||
kCharTable[value]);
|
||||
}
|
||||
}
|
||||
|
||||
// Slow case.
|
||||
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
|
||||
if (std::isnan(value)) {
|
||||
return isolate->heap()->nan_string();
|
||||
}
|
||||
if (std::isinf(value)) {
|
||||
if (value < 0) {
|
||||
return isolate->heap()->minus_infinity_string();
|
||||
}
|
||||
return isolate->heap()->infinity_string();
|
||||
}
|
||||
char* str = DoubleToRadixCString(value, radix);
|
||||
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
|
||||
DeleteArray(str);
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToFixed) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
|
||||
int f = FastD2IChecked(f_number);
|
||||
// See DoubleToFixedCString for these constants:
|
||||
RUNTIME_ASSERT(f >= 0 && f <= 20);
|
||||
RUNTIME_ASSERT(!Double(value).IsSpecial());
|
||||
char* str = DoubleToFixedCString(value, f);
|
||||
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
|
||||
DeleteArray(str);
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToExponential) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
|
||||
int f = FastD2IChecked(f_number);
|
||||
RUNTIME_ASSERT(f >= -1 && f <= 20);
|
||||
RUNTIME_ASSERT(!Double(value).IsSpecial());
|
||||
char* str = DoubleToExponentialCString(value, f);
|
||||
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
|
||||
DeleteArray(str);
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToPrecision) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
|
||||
int f = FastD2IChecked(f_number);
|
||||
RUNTIME_ASSERT(f >= 1 && f <= 21);
|
||||
RUNTIME_ASSERT(!Double(value).IsSpecial());
|
||||
char* str = DoubleToPrecisionCString(value, f);
|
||||
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
|
||||
DeleteArray(str);
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_IsValidSmi) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, number, Int32, args[0]);
|
||||
return isolate->heap()->ToBoolean(Smi::IsValid(number));
|
||||
}
|
||||
|
||||
|
||||
static bool AreDigits(const uint8_t* s, int from, int to) {
|
||||
for (int i = from; i < to; i++) {
|
||||
if (s[i] < '0' || s[i] > '9') return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int ParseDecimalInteger(const uint8_t* s, int from, int to) {
|
||||
DCHECK(to - from < 10); // Overflow is not possible.
|
||||
DCHECK(from < to);
|
||||
int d = s[from] - '0';
|
||||
|
||||
for (int i = from + 1; i < to; i++) {
|
||||
d = 10 * d + (s[i] - '0');
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StringToNumber) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
|
||||
subject = String::Flatten(subject);
|
||||
|
||||
// Fast case: short integer or some sorts of junk values.
|
||||
if (subject->IsSeqOneByteString()) {
|
||||
int len = subject->length();
|
||||
if (len == 0) return Smi::FromInt(0);
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
uint8_t const* data = Handle<SeqOneByteString>::cast(subject)->GetChars();
|
||||
bool minus = (data[0] == '-');
|
||||
int start_pos = (minus ? 1 : 0);
|
||||
|
||||
if (start_pos == len) {
|
||||
return isolate->heap()->nan_value();
|
||||
} else if (data[start_pos] > '9') {
|
||||
// Fast check for a junk value. A valid string may start from a
|
||||
// whitespace, a sign ('+' or '-'), the decimal point, a decimal digit
|
||||
// or the 'I' character ('Infinity'). All of that have codes not greater
|
||||
// than '9' except 'I' and .
|
||||
if (data[start_pos] != 'I' && data[start_pos] != 0xa0) {
|
||||
return isolate->heap()->nan_value();
|
||||
}
|
||||
} else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
|
||||
// The maximal/minimal smi has 10 digits. If the string has less digits
|
||||
// we know it will fit into the smi-data type.
|
||||
int d = ParseDecimalInteger(data, start_pos, len);
|
||||
if (minus) {
|
||||
if (d == 0) return isolate->heap()->minus_zero_value();
|
||||
d = -d;
|
||||
} else if (!subject->HasHashCode() && len <= String::kMaxArrayIndexSize &&
|
||||
(len == 1 || data[0] != '0')) {
|
||||
// String hash is not calculated yet but all the data are present.
|
||||
// Update the hash field to speed up sequential convertions.
|
||||
uint32_t hash = StringHasher::MakeArrayIndexHash(d, len);
|
||||
#ifdef DEBUG
|
||||
subject->Hash(); // Force hash calculation.
|
||||
DCHECK_EQ(static_cast<int>(subject->hash_field()),
|
||||
static_cast<int>(hash));
|
||||
#endif
|
||||
subject->set_hash_field(hash);
|
||||
}
|
||||
return Smi::FromInt(d);
|
||||
}
|
||||
}
|
||||
|
||||
// Slower case.
|
||||
int flags = ALLOW_HEX;
|
||||
if (FLAG_harmony_numeric_literals) {
|
||||
// The current spec draft has not updated "ToNumber Applied to the String
|
||||
// Type", https://bugs.ecmascript.org/show_bug.cgi?id=1584
|
||||
flags |= ALLOW_OCTAL | ALLOW_BINARY;
|
||||
}
|
||||
|
||||
return *isolate->factory()->NewNumber(
|
||||
StringToDouble(isolate->unicode_cache(), *subject, flags));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StringParseInt) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
|
||||
CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]);
|
||||
RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
|
||||
|
||||
subject = String::Flatten(subject);
|
||||
double value;
|
||||
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
String::FlatContent flat = subject->GetFlatContent();
|
||||
|
||||
// ECMA-262 section 15.1.2.3, empty string is NaN
|
||||
if (flat.IsOneByte()) {
|
||||
value =
|
||||
StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(), radix);
|
||||
} else {
|
||||
value = StringToInt(isolate->unicode_cache(), flat.ToUC16Vector(), radix);
|
||||
}
|
||||
}
|
||||
|
||||
return *isolate->factory()->NewNumber(value);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StringParseFloat) {
|
||||
HandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
|
||||
|
||||
subject = String::Flatten(subject);
|
||||
double value = StringToDouble(isolate->unicode_cache(), *subject,
|
||||
ALLOW_TRAILING_JUNK, base::OS::nan_value());
|
||||
|
||||
return *isolate->factory()->NewNumber(value);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToStringRT) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
|
||||
|
||||
return *isolate->factory()->NumberToString(number);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
|
||||
|
||||
return *isolate->factory()->NumberToString(number, false);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToInteger) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(number, 0);
|
||||
return *isolate->factory()->NewNumber(DoubleToInteger(number));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToIntegerMapMinusZero) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(number, 0);
|
||||
double double_value = DoubleToInteger(number);
|
||||
// Map both -0 and +0 to +0.
|
||||
if (double_value == 0) double_value = 0;
|
||||
|
||||
return *isolate->factory()->NewNumber(double_value);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToJSUint32) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
|
||||
return *isolate->factory()->NewNumberFromUint(number);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberToJSInt32) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(number, 0);
|
||||
return *isolate->factory()->NewNumberFromInt(DoubleToInt32(number));
|
||||
}
|
||||
|
||||
|
||||
// Converts a Number to a Smi, if possible. Returns NaN if the number is not
|
||||
// a small integer.
|
||||
RUNTIME_FUNCTION(Runtime_NumberToSmi) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(Object, obj, 0);
|
||||
if (obj->IsSmi()) {
|
||||
return obj;
|
||||
}
|
||||
if (obj->IsHeapNumber()) {
|
||||
double value = HeapNumber::cast(obj)->value();
|
||||
int int_value = FastD2I(value);
|
||||
if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
|
||||
return Smi::FromInt(int_value);
|
||||
}
|
||||
}
|
||||
return isolate->heap()->nan_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberAdd) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
return *isolate->factory()->NewNumber(x + y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberSub) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
return *isolate->factory()->NewNumber(x - y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberMul) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
return *isolate->factory()->NewNumber(x * y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberUnaryMinus) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return *isolate->factory()->NewNumber(-x);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberDiv) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
return *isolate->factory()->NewNumber(x / y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberMod) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
return *isolate->factory()->NewNumber(modulo(x, y));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberImul) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
// We rely on implementation-defined behavior below, but at least not on
|
||||
// undefined behavior.
|
||||
CONVERT_NUMBER_CHECKED(uint32_t, x, Int32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(uint32_t, y, Int32, args[1]);
|
||||
int32_t product = static_cast<int32_t>(x * y);
|
||||
return *isolate->factory()->NewNumberFromInt(product);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberOr) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
|
||||
return *isolate->factory()->NewNumberFromInt(x | y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberAnd) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
|
||||
return *isolate->factory()->NewNumberFromInt(x & y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberXor) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
|
||||
return *isolate->factory()->NewNumberFromInt(x ^ y);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberShl) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
|
||||
return *isolate->factory()->NewNumberFromInt(x << (y & 0x1f));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberShr) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
|
||||
return *isolate->factory()->NewNumberFromUint(x >> (y & 0x1f));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberSar) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
|
||||
return *isolate->factory()->NewNumberFromInt(
|
||||
ArithmeticShiftRight(x, y & 0x1f));
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberEquals) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
if (std::isnan(x)) return Smi::FromInt(NOT_EQUAL);
|
||||
if (std::isnan(y)) return Smi::FromInt(NOT_EQUAL);
|
||||
if (x == y) return Smi::FromInt(EQUAL);
|
||||
Object* result;
|
||||
if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
|
||||
result = Smi::FromInt(EQUAL);
|
||||
} else {
|
||||
result = Smi::FromInt(NOT_EQUAL);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NumberCompare) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(y, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, uncomparable_result, 2)
|
||||
if (std::isnan(x) || std::isnan(y)) return *uncomparable_result;
|
||||
if (x == y) return Smi::FromInt(EQUAL);
|
||||
if (isless(x, y)) return Smi::FromInt(LESS);
|
||||
return Smi::FromInt(GREATER);
|
||||
}
|
||||
|
||||
|
||||
// Compare two Smis as if they were converted to strings and then
|
||||
// compared lexicographically.
|
||||
RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_SMI_ARG_CHECKED(x_value, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(y_value, 1);
|
||||
|
||||
// If the integers are equal so are the string representations.
|
||||
if (x_value == y_value) return Smi::FromInt(EQUAL);
|
||||
|
||||
// If one of the integers is zero the normal integer order is the
|
||||
// same as the lexicographic order of the string representations.
|
||||
if (x_value == 0 || y_value == 0)
|
||||
return Smi::FromInt(x_value < y_value ? LESS : GREATER);
|
||||
|
||||
// If only one of the integers is negative the negative number is
|
||||
// smallest because the char code of '-' is less than the char code
|
||||
// of any digit. Otherwise, we make both values positive.
|
||||
|
||||
// Use unsigned values otherwise the logic is incorrect for -MIN_INT on
|
||||
// architectures using 32-bit Smis.
|
||||
uint32_t x_scaled = x_value;
|
||||
uint32_t y_scaled = y_value;
|
||||
if (x_value < 0 || y_value < 0) {
|
||||
if (y_value >= 0) return Smi::FromInt(LESS);
|
||||
if (x_value >= 0) return Smi::FromInt(GREATER);
|
||||
x_scaled = -x_value;
|
||||
y_scaled = -y_value;
|
||||
}
|
||||
|
||||
static const uint32_t kPowersOf10[] = {
|
||||
1, 10, 100, 1000,
|
||||
10 * 1000, 100 * 1000, 1000 * 1000, 10 * 1000 * 1000,
|
||||
100 * 1000 * 1000, 1000 * 1000 * 1000};
|
||||
|
||||
// If the integers have the same number of decimal digits they can be
|
||||
// compared directly as the numeric order is the same as the
|
||||
// lexicographic order. If one integer has fewer digits, it is scaled
|
||||
// by some power of 10 to have the same number of digits as the longer
|
||||
// integer. If the scaled integers are equal it means the shorter
|
||||
// integer comes first in the lexicographic order.
|
||||
|
||||
// From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
|
||||
int x_log2 = IntegerLog2(x_scaled);
|
||||
int x_log10 = ((x_log2 + 1) * 1233) >> 12;
|
||||
x_log10 -= x_scaled < kPowersOf10[x_log10];
|
||||
|
||||
int y_log2 = IntegerLog2(y_scaled);
|
||||
int y_log10 = ((y_log2 + 1) * 1233) >> 12;
|
||||
y_log10 -= y_scaled < kPowersOf10[y_log10];
|
||||
|
||||
int tie = EQUAL;
|
||||
|
||||
if (x_log10 < y_log10) {
|
||||
// X has fewer digits. We would like to simply scale up X but that
|
||||
// might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
|
||||
// be scaled up to 9_000_000_000. So we scale up by the next
|
||||
// smallest power and scale down Y to drop one digit. It is OK to
|
||||
// drop one digit from the longer integer since the final digit is
|
||||
// past the length of the shorter integer.
|
||||
x_scaled *= kPowersOf10[y_log10 - x_log10 - 1];
|
||||
y_scaled /= 10;
|
||||
tie = LESS;
|
||||
} else if (y_log10 < x_log10) {
|
||||
y_scaled *= kPowersOf10[x_log10 - y_log10 - 1];
|
||||
x_scaled /= 10;
|
||||
tie = GREATER;
|
||||
}
|
||||
|
||||
if (x_scaled < y_scaled) return Smi::FromInt(LESS);
|
||||
if (x_scaled > y_scaled) return Smi::FromInt(GREATER);
|
||||
return Smi::FromInt(tie);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(RuntimeReference_NumberToString) {
|
||||
SealHandleScope shs(isolate);
|
||||
return __RT_impl_Runtime_NumberToStringRT(args, isolate);
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
@ -1182,6 +1182,14 @@ RUNTIME_FUNCTION(Runtime_StringEquals) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_FlattenString) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
|
||||
return *String::Flatten(str);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(RuntimeReference_StringCharFromCode) {
|
||||
SealHandleScope shs(isolate);
|
||||
return __RT_impl_Runtime_CharFromCode(args, isolate);
|
||||
|
323
src/runtime/runtime-test.cc
Normal file
323
src/runtime/runtime-test.cc
Normal file
@ -0,0 +1,323 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/full-codegen.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
if (!function->IsOptimized()) return isolate->heap()->undefined_value();
|
||||
|
||||
// TODO(turbofan): Deoptimization is not supported yet.
|
||||
if (function->code()->is_turbofanned() && !FLAG_turbo_deoptimization) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
Deoptimizer::DeoptimizeFunction(*function);
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
#if defined(USE_SIMULATOR)
|
||||
return isolate->heap()->true_value();
|
||||
#else
|
||||
return isolate->heap()->false_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_IsConcurrentRecompilationSupported) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
return isolate->heap()->ToBoolean(
|
||||
isolate->concurrent_recompilation_enabled());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) {
|
||||
HandleScope scope(isolate);
|
||||
RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
// The following two assertions are lifted from the DCHECKs inside
|
||||
// JSFunction::MarkForOptimization().
|
||||
RUNTIME_ASSERT(!function->shared()->is_generator());
|
||||
RUNTIME_ASSERT(function->shared()->allows_lazy_compilation() ||
|
||||
(function->code()->kind() == Code::FUNCTION &&
|
||||
function->code()->optimizable()));
|
||||
|
||||
// If the function is optimized, just return.
|
||||
if (function->IsOptimized()) return isolate->heap()->undefined_value();
|
||||
|
||||
function->MarkForOptimization();
|
||||
|
||||
Code* unoptimized = function->shared()->code();
|
||||
if (args.length() == 2 && unoptimized->kind() == Code::FUNCTION) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
|
||||
if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("osr")) && FLAG_use_osr) {
|
||||
// Start patching from the currently patched loop nesting level.
|
||||
DCHECK(BackEdgeTable::Verify(isolate, unoptimized));
|
||||
isolate->runtime_profiler()->AttemptOnStackReplacement(
|
||||
*function, Code::kMaxLoopNestingMarker);
|
||||
} else if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("concurrent")) &&
|
||||
isolate->concurrent_recompilation_enabled()) {
|
||||
function->MarkForConcurrentOptimization();
|
||||
}
|
||||
}
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(JSFunction, function, 0);
|
||||
function->shared()->set_optimization_disabled(true);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
|
||||
HandleScope scope(isolate);
|
||||
RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
|
||||
if (!isolate->use_crankshaft()) {
|
||||
return Smi::FromInt(4); // 4 == "never".
|
||||
}
|
||||
bool sync_with_compiler_thread = true;
|
||||
if (args.length() == 2) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, sync, 1);
|
||||
if (sync->IsOneByteEqualTo(STATIC_CHAR_VECTOR("no sync"))) {
|
||||
sync_with_compiler_thread = false;
|
||||
}
|
||||
}
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
if (isolate->concurrent_recompilation_enabled() &&
|
||||
sync_with_compiler_thread) {
|
||||
while (function->IsInOptimizationQueue()) {
|
||||
isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
|
||||
base::OS::Sleep(50);
|
||||
}
|
||||
}
|
||||
if (FLAG_always_opt) {
|
||||
// We may have always opt, but that is more best-effort than a real
|
||||
// promise, so we still say "no" if it is not optimized.
|
||||
return function->IsOptimized() ? Smi::FromInt(3) // 3 == "always".
|
||||
: Smi::FromInt(2); // 2 == "no".
|
||||
}
|
||||
if (FLAG_deopt_every_n_times) {
|
||||
return Smi::FromInt(6); // 6 == "maybe deopted".
|
||||
}
|
||||
if (function->IsOptimized() && function->code()->is_turbofanned()) {
|
||||
return Smi::FromInt(7); // 7 == "TurboFan compiler".
|
||||
}
|
||||
return function->IsOptimized() ? Smi::FromInt(1) // 1 == "yes".
|
||||
: Smi::FromInt(2); // 2 == "no".
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_UnblockConcurrentRecompilation) {
|
||||
DCHECK(args.length() == 0);
|
||||
RUNTIME_ASSERT(FLAG_block_concurrent_recompilation);
|
||||
RUNTIME_ASSERT(isolate->concurrent_recompilation_enabled());
|
||||
isolate->optimizing_compiler_thread()->Unblock();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetOptimizationCount) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
return Smi::FromInt(function->shared()->opt_count());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ClearFunctionTypeFeedback) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
function->shared()->ClearTypeFeedbackInfo();
|
||||
Code* unoptimized = function->shared()->code();
|
||||
if (unoptimized->kind() == Code::FUNCTION) {
|
||||
unoptimized->ClearInlineCaches();
|
||||
}
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NotifyContextDisposed) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
isolate->heap()->NotifyContextDisposed();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetAllocationTimeout) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 2 || args.length() == 3);
|
||||
#ifdef DEBUG
|
||||
CONVERT_SMI_ARG_CHECKED(interval, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(timeout, 1);
|
||||
isolate->heap()->set_allocation_timeout(timeout);
|
||||
FLAG_gc_interval = interval;
|
||||
if (args.length() == 3) {
|
||||
// Enable/disable inline allocation if requested.
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(inline_allocation, 2);
|
||||
if (inline_allocation) {
|
||||
isolate->heap()->EnableInlineAllocation();
|
||||
} else {
|
||||
isolate->heap()->DisableInlineAllocation();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DebugPrint) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
OFStream os(stdout);
|
||||
#ifdef DEBUG
|
||||
if (args[0]->IsString()) {
|
||||
// If we have a string, assume it's a code "marker"
|
||||
// and print some interesting cpu debugging info.
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
os << "fp = " << frame->fp() << ", sp = " << frame->sp()
|
||||
<< ", caller_sp = " << frame->caller_sp() << ": ";
|
||||
} else {
|
||||
os << "DebugPrint: ";
|
||||
}
|
||||
args[0]->Print(os);
|
||||
if (args[0]->IsHeapObject()) {
|
||||
os << "\n";
|
||||
HeapObject::cast(args[0])->map()->Print(os);
|
||||
}
|
||||
#else
|
||||
// ShortPrint is available in release mode. Print is not.
|
||||
os << Brief(args[0]);
|
||||
#endif
|
||||
os << endl;
|
||||
|
||||
return args[0]; // return TOS
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DebugTrace) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
isolate->PrintStack(stdout);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
// This will not allocate (flatten the string), but it may run
|
||||
// very slowly for very deeply nested ConsStrings. For debugging use only.
|
||||
RUNTIME_FUNCTION(Runtime_GlobalPrint) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
||||
CONVERT_ARG_CHECKED(String, string, 0);
|
||||
ConsStringIteratorOp op;
|
||||
StringCharacterStream stream(string, &op);
|
||||
while (stream.HasMore()) {
|
||||
uint16_t character = stream.GetNext();
|
||||
PrintF("%c", character);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SystemBreak) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 0);
|
||||
base::OS::DebugBreak();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
// Sets a v8 flag.
|
||||
RUNTIME_FUNCTION(Runtime_SetFlags) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(String, arg, 0);
|
||||
SmartArrayPointer<char> flags =
|
||||
arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
||||
FlagList::SetFlagsFromString(flags.get(), StrLength(flags.get()));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_Abort) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_SMI_ARG_CHECKED(message_id, 0);
|
||||
const char* message =
|
||||
GetBailoutReason(static_cast<BailoutReason>(message_id));
|
||||
base::OS::PrintError("abort: %s\n", message);
|
||||
isolate->PrintStack(stderr);
|
||||
base::OS::Abort();
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AbortJS) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, message, 0);
|
||||
base::OS::PrintError("abort: %s\n", message->ToCString().get());
|
||||
isolate->PrintStack(stderr);
|
||||
base::OS::Abort();
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_HaveSameMap) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_CHECKED(JSObject, obj1, 0);
|
||||
CONVERT_ARG_CHECKED(JSObject, obj2, 1);
|
||||
return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
|
||||
}
|
||||
|
||||
|
||||
#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
|
||||
RUNTIME_FUNCTION(Runtime_Has##Name) { \
|
||||
CONVERT_ARG_CHECKED(JSObject, obj, 0); \
|
||||
return isolate->heap()->ToBoolean(obj->Has##Name()); \
|
||||
}
|
||||
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SloppyArgumentsElements)
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalArrayElements)
|
||||
// Properties test sitting with elements tests - not fooling anyone.
|
||||
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastProperties)
|
||||
|
||||
#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
|
||||
}
|
||||
} // namespace v8::internal
|
760
src/runtime/runtime-typedarray.cc
Normal file
760
src/runtime/runtime-typedarray.cc
Normal file
@ -0,0 +1,760 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
void Runtime::FreeArrayBuffer(Isolate* isolate,
|
||||
JSArrayBuffer* phantom_array_buffer) {
|
||||
if (phantom_array_buffer->should_be_freed()) {
|
||||
DCHECK(phantom_array_buffer->is_external());
|
||||
free(phantom_array_buffer->backing_store());
|
||||
}
|
||||
if (phantom_array_buffer->is_external()) return;
|
||||
|
||||
size_t allocated_length =
|
||||
NumberToSize(isolate, phantom_array_buffer->byte_length());
|
||||
|
||||
reinterpret_cast<v8::Isolate*>(isolate)
|
||||
->AdjustAmountOfExternalAllocatedMemory(
|
||||
-static_cast<int64_t>(allocated_length));
|
||||
CHECK(V8::ArrayBufferAllocator() != NULL);
|
||||
V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(),
|
||||
allocated_length);
|
||||
}
|
||||
|
||||
|
||||
void Runtime::SetupArrayBuffer(Isolate* isolate,
|
||||
Handle<JSArrayBuffer> array_buffer,
|
||||
bool is_external, void* data,
|
||||
size_t allocated_length) {
|
||||
DCHECK(array_buffer->GetInternalFieldCount() ==
|
||||
v8::ArrayBuffer::kInternalFieldCount);
|
||||
for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
|
||||
array_buffer->SetInternalField(i, Smi::FromInt(0));
|
||||
}
|
||||
array_buffer->set_backing_store(data);
|
||||
array_buffer->set_flag(Smi::FromInt(0));
|
||||
array_buffer->set_is_external(is_external);
|
||||
|
||||
Handle<Object> byte_length =
|
||||
isolate->factory()->NewNumberFromSize(allocated_length);
|
||||
CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
|
||||
array_buffer->set_byte_length(*byte_length);
|
||||
|
||||
array_buffer->set_weak_next(isolate->heap()->array_buffers_list());
|
||||
isolate->heap()->set_array_buffers_list(*array_buffer);
|
||||
array_buffer->set_weak_first_view(isolate->heap()->undefined_value());
|
||||
}
|
||||
|
||||
|
||||
bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate,
|
||||
Handle<JSArrayBuffer> array_buffer,
|
||||
size_t allocated_length,
|
||||
bool initialize) {
|
||||
void* data;
|
||||
CHECK(V8::ArrayBufferAllocator() != NULL);
|
||||
if (allocated_length != 0) {
|
||||
if (initialize) {
|
||||
data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
|
||||
} else {
|
||||
data =
|
||||
V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length);
|
||||
}
|
||||
if (data == NULL) return false;
|
||||
} else {
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
|
||||
|
||||
reinterpret_cast<v8::Isolate*>(isolate)
|
||||
->AdjustAmountOfExternalAllocatedMemory(allocated_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) {
|
||||
Isolate* isolate = array_buffer->GetIsolate();
|
||||
for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate);
|
||||
!view_obj->IsUndefined();) {
|
||||
Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj));
|
||||
if (view->IsJSTypedArray()) {
|
||||
JSTypedArray::cast(*view)->Neuter();
|
||||
} else if (view->IsJSDataView()) {
|
||||
JSDataView::cast(*view)->Neuter();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
view_obj = handle(view->weak_next(), isolate);
|
||||
}
|
||||
array_buffer->Neuter();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1);
|
||||
if (!holder->byte_length()->IsUndefined()) {
|
||||
// ArrayBuffer is already initialized; probably a fuzz test.
|
||||
return *holder;
|
||||
}
|
||||
size_t allocated_length = 0;
|
||||
if (!TryNumberToSize(isolate, *byteLength, &allocated_length)) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError("invalid_array_buffer_length",
|
||||
HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder,
|
||||
allocated_length)) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError("invalid_array_buffer_length",
|
||||
HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
return *holder;
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
|
||||
return holder->byte_length();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
|
||||
RUNTIME_ASSERT(!source.is_identical_to(target));
|
||||
size_t start = 0;
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *first, &start));
|
||||
size_t target_length = NumberToSize(isolate, target->byte_length());
|
||||
|
||||
if (target_length == 0) return isolate->heap()->undefined_value();
|
||||
|
||||
size_t source_byte_length = NumberToSize(isolate, source->byte_length());
|
||||
RUNTIME_ASSERT(start <= source_byte_length);
|
||||
RUNTIME_ASSERT(source_byte_length - start >= target_length);
|
||||
uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
|
||||
uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
|
||||
CopyBytes(target_data, source_data + start, target_length);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_CHECKED(Object, object, 0);
|
||||
return isolate->heap()->ToBoolean(object->IsJSArrayBufferView());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
|
||||
if (array_buffer->backing_store() == NULL) {
|
||||
CHECK(Smi::FromInt(0) == array_buffer->byte_length());
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
DCHECK(!array_buffer->is_external());
|
||||
void* backing_store = array_buffer->backing_store();
|
||||
size_t byte_length = NumberToSize(isolate, array_buffer->byte_length());
|
||||
array_buffer->set_is_external(true);
|
||||
Runtime::NeuterArrayBuffer(array_buffer);
|
||||
V8::ArrayBufferAllocator()->Free(backing_store, byte_length);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
|
||||
ElementsKind* external_elements_kind,
|
||||
ElementsKind* fixed_elements_kind,
|
||||
size_t* element_size) {
|
||||
switch (arrayId) {
|
||||
#define ARRAY_ID_CASE(Type, type, TYPE, ctype, size) \
|
||||
case ARRAY_ID_##TYPE: \
|
||||
*array_type = kExternal##Type##Array; \
|
||||
*external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \
|
||||
*fixed_elements_kind = TYPE##_ELEMENTS; \
|
||||
*element_size = size; \
|
||||
break;
|
||||
|
||||
TYPED_ARRAYS(ARRAY_ID_CASE)
|
||||
#undef ARRAY_ID_CASE
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 5);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(arrayId, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4);
|
||||
|
||||
RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
|
||||
arrayId <= Runtime::ARRAY_ID_LAST);
|
||||
|
||||
ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
|
||||
size_t element_size = 1; // Bogus initialization.
|
||||
ElementsKind external_elements_kind =
|
||||
EXTERNAL_INT8_ELEMENTS; // Bogus initialization.
|
||||
ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
|
||||
Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
|
||||
&fixed_elements_kind, &element_size);
|
||||
RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
|
||||
|
||||
size_t byte_offset = 0;
|
||||
size_t byte_length = 0;
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset_object, &byte_offset));
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length_object, &byte_length));
|
||||
|
||||
if (maybe_buffer->IsJSArrayBuffer()) {
|
||||
Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
|
||||
size_t array_buffer_byte_length =
|
||||
NumberToSize(isolate, buffer->byte_length());
|
||||
RUNTIME_ASSERT(byte_offset <= array_buffer_byte_length);
|
||||
RUNTIME_ASSERT(array_buffer_byte_length - byte_offset >= byte_length);
|
||||
} else {
|
||||
RUNTIME_ASSERT(maybe_buffer->IsNull());
|
||||
}
|
||||
|
||||
RUNTIME_ASSERT(byte_length % element_size == 0);
|
||||
size_t length = byte_length / element_size;
|
||||
|
||||
if (length > static_cast<unsigned>(Smi::kMaxValue)) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError("invalid_typed_array_length",
|
||||
HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
|
||||
// All checks are done, now we can modify objects.
|
||||
|
||||
DCHECK(holder->GetInternalFieldCount() ==
|
||||
v8::ArrayBufferView::kInternalFieldCount);
|
||||
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
|
||||
holder->SetInternalField(i, Smi::FromInt(0));
|
||||
}
|
||||
Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
|
||||
holder->set_length(*length_obj);
|
||||
holder->set_byte_offset(*byte_offset_object);
|
||||
holder->set_byte_length(*byte_length_object);
|
||||
|
||||
if (!maybe_buffer->IsNull()) {
|
||||
Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
|
||||
holder->set_buffer(*buffer);
|
||||
holder->set_weak_next(buffer->weak_first_view());
|
||||
buffer->set_weak_first_view(*holder);
|
||||
|
||||
Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
|
||||
static_cast<int>(length), array_type,
|
||||
static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
|
||||
Handle<Map> map =
|
||||
JSObject::GetElementsTransitionMap(holder, external_elements_kind);
|
||||
JSObject::SetMapAndElements(holder, map, elements);
|
||||
DCHECK(IsExternalArrayElementsKind(holder->map()->elements_kind()));
|
||||
} else {
|
||||
holder->set_buffer(Smi::FromInt(0));
|
||||
holder->set_weak_next(isolate->heap()->undefined_value());
|
||||
Handle<FixedTypedArrayBase> elements =
|
||||
isolate->factory()->NewFixedTypedArray(static_cast<int>(length),
|
||||
array_type);
|
||||
holder->set_elements(*elements);
|
||||
}
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
// Initializes a typed array from an array-like object.
|
||||
// If an array-like object happens to be a typed array of the same type,
|
||||
// initializes backing store using memove.
|
||||
//
|
||||
// Returns true if backing store was initialized or false otherwise.
|
||||
RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 4);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(arrayId, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3);
|
||||
|
||||
RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
|
||||
arrayId <= Runtime::ARRAY_ID_LAST);
|
||||
|
||||
ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
|
||||
size_t element_size = 1; // Bogus initialization.
|
||||
ElementsKind external_elements_kind =
|
||||
EXTERNAL_INT8_ELEMENTS; // Bogus intialization.
|
||||
ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
|
||||
Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &external_elements_kind,
|
||||
&fixed_elements_kind, &element_size);
|
||||
|
||||
RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
|
||||
|
||||
Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
|
||||
if (source->IsJSTypedArray() &&
|
||||
JSTypedArray::cast(*source)->type() == array_type) {
|
||||
length_obj = Handle<Object>(JSTypedArray::cast(*source)->length(), isolate);
|
||||
}
|
||||
size_t length = 0;
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length));
|
||||
|
||||
if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
|
||||
(length > (kMaxInt / element_size))) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError("invalid_typed_array_length",
|
||||
HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
size_t byte_length = length * element_size;
|
||||
|
||||
DCHECK(holder->GetInternalFieldCount() ==
|
||||
v8::ArrayBufferView::kInternalFieldCount);
|
||||
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
|
||||
holder->SetInternalField(i, Smi::FromInt(0));
|
||||
}
|
||||
|
||||
// NOTE: not initializing backing store.
|
||||
// We assume that the caller of this function will initialize holder
|
||||
// with the loop
|
||||
// for(i = 0; i < length; i++) { holder[i] = source[i]; }
|
||||
// We assume that the caller of this function is always a typed array
|
||||
// constructor.
|
||||
// If source is a typed array, this loop will always run to completion,
|
||||
// so we are sure that the backing store will be initialized.
|
||||
// Otherwise, the indexing operation might throw, so the loop will not
|
||||
// run to completion and the typed array might remain partly initialized.
|
||||
// However we further assume that the caller of this function is a typed array
|
||||
// constructor, and the exception will propagate out of the constructor,
|
||||
// therefore uninitialized memory will not be accessible by a user program.
|
||||
//
|
||||
// TODO(dslomov): revise this once we support subclassing.
|
||||
|
||||
if (!Runtime::SetupArrayBufferAllocatingData(isolate, buffer, byte_length,
|
||||
false)) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError("invalid_array_buffer_length",
|
||||
HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
|
||||
holder->set_buffer(*buffer);
|
||||
holder->set_byte_offset(Smi::FromInt(0));
|
||||
Handle<Object> byte_length_obj(
|
||||
isolate->factory()->NewNumberFromSize(byte_length));
|
||||
holder->set_byte_length(*byte_length_obj);
|
||||
holder->set_length(*length_obj);
|
||||
holder->set_weak_next(buffer->weak_first_view());
|
||||
buffer->set_weak_first_view(*holder);
|
||||
|
||||
Handle<ExternalArray> elements = isolate->factory()->NewExternalArray(
|
||||
static_cast<int>(length), array_type,
|
||||
static_cast<uint8_t*>(buffer->backing_store()));
|
||||
Handle<Map> map =
|
||||
JSObject::GetElementsTransitionMap(holder, external_elements_kind);
|
||||
JSObject::SetMapAndElements(holder, map, elements);
|
||||
|
||||
if (source->IsJSTypedArray()) {
|
||||
Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
|
||||
|
||||
if (typed_array->type() == holder->type()) {
|
||||
uint8_t* backing_store =
|
||||
static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store());
|
||||
size_t source_byte_offset =
|
||||
NumberToSize(isolate, typed_array->byte_offset());
|
||||
memcpy(buffer->backing_store(), backing_store + source_byte_offset,
|
||||
byte_length);
|
||||
return isolate->heap()->true_value();
|
||||
}
|
||||
}
|
||||
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
|
||||
|
||||
#define BUFFER_VIEW_GETTER(Type, getter, accessor) \
|
||||
RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK(args.length() == 1); \
|
||||
CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
|
||||
return holder->accessor(); \
|
||||
}
|
||||
|
||||
BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
|
||||
BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
|
||||
BUFFER_VIEW_GETTER(TypedArray, Length, length)
|
||||
BUFFER_VIEW_GETTER(DataView, Buffer, buffer)
|
||||
|
||||
#undef BUFFER_VIEW_GETTER
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
|
||||
return *holder->GetBuffer();
|
||||
}
|
||||
|
||||
|
||||
// Return codes for Runtime_TypedArraySetFastCases.
|
||||
// Should be synchronized with typedarray.js natives.
|
||||
enum TypedArraySetResultCodes {
|
||||
// Set from typed array of the same type.
|
||||
// This is processed by TypedArraySetFastCases
|
||||
TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
|
||||
// Set from typed array of the different type, overlapping in memory.
|
||||
TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
|
||||
// Set from typed array of the different type, non-overlapping.
|
||||
TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
|
||||
// Set from non-typed array.
|
||||
TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
|
||||
};
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
if (!args[0]->IsJSTypedArray()) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate,
|
||||
NewTypeError("not_typed_array", HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
|
||||
if (!args[1]->IsJSTypedArray())
|
||||
return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2);
|
||||
|
||||
Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
|
||||
Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
|
||||
size_t offset = 0;
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *offset_obj, &offset));
|
||||
size_t target_length = NumberToSize(isolate, target->length());
|
||||
size_t source_length = NumberToSize(isolate, source->length());
|
||||
size_t target_byte_length = NumberToSize(isolate, target->byte_length());
|
||||
size_t source_byte_length = NumberToSize(isolate, source->byte_length());
|
||||
if (offset > target_length || offset + source_length > target_length ||
|
||||
offset + source_length < offset) { // overflow
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewRangeError("typed_array_set_source_too_large",
|
||||
HandleVector<Object>(NULL, 0)));
|
||||
}
|
||||
|
||||
size_t target_offset = NumberToSize(isolate, target->byte_offset());
|
||||
size_t source_offset = NumberToSize(isolate, source->byte_offset());
|
||||
uint8_t* target_base =
|
||||
static_cast<uint8_t*>(target->GetBuffer()->backing_store()) +
|
||||
target_offset;
|
||||
uint8_t* source_base =
|
||||
static_cast<uint8_t*>(source->GetBuffer()->backing_store()) +
|
||||
source_offset;
|
||||
|
||||
// Typed arrays of the same type: use memmove.
|
||||
if (target->type() == source->type()) {
|
||||
memmove(target_base + offset * target->element_size(), source_base,
|
||||
source_byte_length);
|
||||
return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
|
||||
}
|
||||
|
||||
// Typed arrays of different types over the same backing store
|
||||
if ((source_base <= target_base &&
|
||||
source_base + source_byte_length > target_base) ||
|
||||
(target_base <= source_base &&
|
||||
target_base + target_byte_length > source_base)) {
|
||||
// We do not support overlapping ArrayBuffers
|
||||
DCHECK(target->GetBuffer()->backing_store() ==
|
||||
source->GetBuffer()->backing_store());
|
||||
return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
|
||||
} else { // Non-overlapping typed arrays
|
||||
return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) {
|
||||
DCHECK(args.length() == 0);
|
||||
DCHECK_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap +
|
||||
FixedTypedArrayBase::kDataOffset);
|
||||
return Smi::FromInt(FLAG_typed_array_max_size_in_heap);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DataViewInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 4);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3);
|
||||
|
||||
DCHECK(holder->GetInternalFieldCount() ==
|
||||
v8::ArrayBufferView::kInternalFieldCount);
|
||||
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
|
||||
holder->SetInternalField(i, Smi::FromInt(0));
|
||||
}
|
||||
size_t buffer_length = 0;
|
||||
size_t offset = 0;
|
||||
size_t length = 0;
|
||||
RUNTIME_ASSERT(
|
||||
TryNumberToSize(isolate, buffer->byte_length(), &buffer_length));
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset, &offset));
|
||||
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length, &length));
|
||||
|
||||
// TODO(jkummerow): When we have a "safe numerics" helper class, use it here.
|
||||
// Entire range [offset, offset + length] must be in bounds.
|
||||
RUNTIME_ASSERT(offset <= buffer_length);
|
||||
RUNTIME_ASSERT(offset + length <= buffer_length);
|
||||
// No overflow.
|
||||
RUNTIME_ASSERT(offset + length >= offset);
|
||||
|
||||
holder->set_buffer(*buffer);
|
||||
holder->set_byte_offset(*byte_offset);
|
||||
holder->set_byte_length(*byte_length);
|
||||
|
||||
holder->set_weak_next(buffer->weak_first_view());
|
||||
buffer->set_weak_first_view(*holder);
|
||||
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
inline static bool NeedToFlipBytes(bool is_little_endian) {
|
||||
#ifdef V8_TARGET_LITTLE_ENDIAN
|
||||
return !is_little_endian;
|
||||
#else
|
||||
return is_little_endian;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template <int n>
|
||||
inline void CopyBytes(uint8_t* target, uint8_t* source) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
*(target++) = *(source++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <int n>
|
||||
inline void FlipBytes(uint8_t* target, uint8_t* source) {
|
||||
source = source + (n - 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
*(target++) = *(source--);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline static bool DataViewGetValue(Isolate* isolate,
|
||||
Handle<JSDataView> data_view,
|
||||
Handle<Object> byte_offset_obj,
|
||||
bool is_little_endian, T* result) {
|
||||
size_t byte_offset = 0;
|
||||
if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
|
||||
return false;
|
||||
}
|
||||
Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
|
||||
|
||||
size_t data_view_byte_offset =
|
||||
NumberToSize(isolate, data_view->byte_offset());
|
||||
size_t data_view_byte_length =
|
||||
NumberToSize(isolate, data_view->byte_length());
|
||||
if (byte_offset + sizeof(T) > data_view_byte_length ||
|
||||
byte_offset + sizeof(T) < byte_offset) { // overflow
|
||||
return false;
|
||||
}
|
||||
|
||||
union Value {
|
||||
T data;
|
||||
uint8_t bytes[sizeof(T)];
|
||||
};
|
||||
|
||||
Value value;
|
||||
size_t buffer_offset = data_view_byte_offset + byte_offset;
|
||||
DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
|
||||
buffer_offset + sizeof(T));
|
||||
uint8_t* source =
|
||||
static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
|
||||
if (NeedToFlipBytes(is_little_endian)) {
|
||||
FlipBytes<sizeof(T)>(value.bytes, source);
|
||||
} else {
|
||||
CopyBytes<sizeof(T)>(value.bytes, source);
|
||||
}
|
||||
*result = value.data;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static bool DataViewSetValue(Isolate* isolate, Handle<JSDataView> data_view,
|
||||
Handle<Object> byte_offset_obj,
|
||||
bool is_little_endian, T data) {
|
||||
size_t byte_offset = 0;
|
||||
if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
|
||||
return false;
|
||||
}
|
||||
Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
|
||||
|
||||
size_t data_view_byte_offset =
|
||||
NumberToSize(isolate, data_view->byte_offset());
|
||||
size_t data_view_byte_length =
|
||||
NumberToSize(isolate, data_view->byte_length());
|
||||
if (byte_offset + sizeof(T) > data_view_byte_length ||
|
||||
byte_offset + sizeof(T) < byte_offset) { // overflow
|
||||
return false;
|
||||
}
|
||||
|
||||
union Value {
|
||||
T data;
|
||||
uint8_t bytes[sizeof(T)];
|
||||
};
|
||||
|
||||
Value value;
|
||||
value.data = data;
|
||||
size_t buffer_offset = data_view_byte_offset + byte_offset;
|
||||
DCHECK(NumberToSize(isolate, buffer->byte_length()) >=
|
||||
buffer_offset + sizeof(T));
|
||||
uint8_t* target =
|
||||
static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
|
||||
if (NeedToFlipBytes(is_little_endian)) {
|
||||
FlipBytes<sizeof(T)>(target, value.bytes);
|
||||
} else {
|
||||
CopyBytes<sizeof(T)>(target, value.bytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define DATA_VIEW_GETTER(TypeName, Type, Converter) \
|
||||
RUNTIME_FUNCTION(Runtime_DataViewGet##TypeName) { \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK(args.length() == 3); \
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2); \
|
||||
Type result; \
|
||||
if (DataViewGetValue(isolate, holder, offset, is_little_endian, \
|
||||
&result)) { \
|
||||
return *isolate->factory()->Converter(result); \
|
||||
} else { \
|
||||
THROW_NEW_ERROR_RETURN_FAILURE( \
|
||||
isolate, NewRangeError("invalid_data_view_accessor_offset", \
|
||||
HandleVector<Object>(NULL, 0))); \
|
||||
} \
|
||||
}
|
||||
|
||||
DATA_VIEW_GETTER(Uint8, uint8_t, NewNumberFromUint)
|
||||
DATA_VIEW_GETTER(Int8, int8_t, NewNumberFromInt)
|
||||
DATA_VIEW_GETTER(Uint16, uint16_t, NewNumberFromUint)
|
||||
DATA_VIEW_GETTER(Int16, int16_t, NewNumberFromInt)
|
||||
DATA_VIEW_GETTER(Uint32, uint32_t, NewNumberFromUint)
|
||||
DATA_VIEW_GETTER(Int32, int32_t, NewNumberFromInt)
|
||||
DATA_VIEW_GETTER(Float32, float, NewNumber)
|
||||
DATA_VIEW_GETTER(Float64, double, NewNumber)
|
||||
|
||||
#undef DATA_VIEW_GETTER
|
||||
|
||||
|
||||
template <typename T>
|
||||
static T DataViewConvertValue(double value);
|
||||
|
||||
|
||||
template <>
|
||||
int8_t DataViewConvertValue<int8_t>(double value) {
|
||||
return static_cast<int8_t>(DoubleToInt32(value));
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
int16_t DataViewConvertValue<int16_t>(double value) {
|
||||
return static_cast<int16_t>(DoubleToInt32(value));
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
int32_t DataViewConvertValue<int32_t>(double value) {
|
||||
return DoubleToInt32(value);
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
uint8_t DataViewConvertValue<uint8_t>(double value) {
|
||||
return static_cast<uint8_t>(DoubleToUint32(value));
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
uint16_t DataViewConvertValue<uint16_t>(double value) {
|
||||
return static_cast<uint16_t>(DoubleToUint32(value));
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
uint32_t DataViewConvertValue<uint32_t>(double value) {
|
||||
return DoubleToUint32(value);
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
float DataViewConvertValue<float>(double value) {
|
||||
return static_cast<float>(value);
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
double DataViewConvertValue<double>(double value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
#define DATA_VIEW_SETTER(TypeName, Type) \
|
||||
RUNTIME_FUNCTION(Runtime_DataViewSet##TypeName) { \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK(args.length() == 4); \
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2); \
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3); \
|
||||
Type v = DataViewConvertValue<Type>(value->Number()); \
|
||||
if (DataViewSetValue(isolate, holder, offset, is_little_endian, v)) { \
|
||||
return isolate->heap()->undefined_value(); \
|
||||
} else { \
|
||||
THROW_NEW_ERROR_RETURN_FAILURE( \
|
||||
isolate, NewRangeError("invalid_data_view_accessor_offset", \
|
||||
HandleVector<Object>(NULL, 0))); \
|
||||
} \
|
||||
}
|
||||
|
||||
DATA_VIEW_SETTER(Uint8, uint8_t)
|
||||
DATA_VIEW_SETTER(Int8, int8_t)
|
||||
DATA_VIEW_SETTER(Uint16, uint16_t)
|
||||
DATA_VIEW_SETTER(Int16, int16_t)
|
||||
DATA_VIEW_SETTER(Uint32, uint32_t)
|
||||
DATA_VIEW_SETTER(Int32, int32_t)
|
||||
DATA_VIEW_SETTER(Float32, float)
|
||||
DATA_VIEW_SETTER(Float64, double)
|
||||
|
||||
#undef DATA_VIEW_SETTER
|
||||
}
|
||||
} // namespace v8::internal
|
File diff suppressed because it is too large
Load Diff
@ -735,10 +735,16 @@
|
||||
'../../src/rewriter.h',
|
||||
'../../src/runtime-profiler.cc',
|
||||
'../../src/runtime-profiler.h',
|
||||
'../../src/runtime/runtime-collections.cc',
|
||||
'../../src/runtime/runtime-compiler.cc',
|
||||
'../../src/runtime/runtime-i18n.cc',
|
||||
'../../src/runtime/runtime-json.cc',
|
||||
'../../src/runtime/runtime-maths.cc',
|
||||
'../../src/runtime/runtime-numbers.cc',
|
||||
'../../src/runtime/runtime-regexp.cc',
|
||||
'../../src/runtime/runtime-strings.cc',
|
||||
'../../src/runtime/runtime-test.cc',
|
||||
'../../src/runtime/runtime-typedarray.cc',
|
||||
'../../src/runtime/runtime-uri.cc',
|
||||
'../../src/runtime/runtime-utils.h',
|
||||
'../../src/runtime/runtime.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user