From b0fdeb1b2a3235fb95df0626ab236944d1596e79 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Mon, 29 Sep 2014 09:32:38 +0000 Subject: [PATCH] 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 --- BUILD.gn | 6 + src/runtime/runtime-collections.cc | 347 ++ src/runtime/runtime-compiler.cc | 441 +++ src/runtime/runtime-maths.cc | 247 ++ src/runtime/runtime-numbers.cc | 565 +++ src/runtime/runtime-strings.cc | 8 + src/runtime/runtime-test.cc | 323 ++ src/runtime/runtime-typedarray.cc | 760 ++++ src/runtime/runtime.cc | 6838 +++++++++++------------------------- tools/gyp/v8.gyp | 6 + 10 files changed, 4825 insertions(+), 4716 deletions(-) create mode 100644 src/runtime/runtime-collections.cc create mode 100644 src/runtime/runtime-compiler.cc create mode 100644 src/runtime/runtime-maths.cc create mode 100644 src/runtime/runtime-numbers.cc create mode 100644 src/runtime/runtime-test.cc create mode 100644 src/runtime/runtime-typedarray.cc diff --git a/BUILD.gn b/BUILD.gn index 8ee3a95..bfe4439 100644 --- a/BUILD.gn +++ b/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", diff --git a/src/runtime/runtime-collections.cc b/src/runtime/runtime-collections.cc new file mode 100644 index 0000000..d0d6aa6 --- /dev/null +++ b/src/runtime/runtime-collections.cc @@ -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 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 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 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 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 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 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 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 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 table(OrderedHashMap::cast(holder->table())); + Handle 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 table(OrderedHashMap::cast(holder->table())); + Handle 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 table(OrderedHashMap::cast(holder->table())); + bool was_present = false; + Handle 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 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 table(OrderedHashMap::cast(holder->table())); + Handle 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 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 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 table(ObjectHashTable::cast(holder->table())); + Handle 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 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 WeakCollectionInitialize( + Isolate* isolate, Handle weak_collection) { + DCHECK(weak_collection->map()->inobject_properties() == 0); + Handle 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 table( + ObjectHashTable::cast(weak_collection->table())); + RUNTIME_ASSERT(table->IsKey(*key)); + Handle 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 table( + ObjectHashTable::cast(weak_collection->table())); + RUNTIME_ASSERT(table->IsKey(*key)); + Handle 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 table( + ObjectHashTable::cast(weak_collection->table())); + RUNTIME_ASSERT(table->IsKey(*key)); + bool was_present = false; + Handle 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 table( + ObjectHashTable::cast(weak_collection->table())); + RUNTIME_ASSERT(table->IsKey(*key)); + Handle 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 table(ObjectHashTable::cast(holder->table())); + Handle 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 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 = + isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize); + Handle weakmap = + Handle::cast(isolate->factory()->NewJSObjectFromMap(map)); + return *WeakCollectionInitialize(isolate, weakmap); +} +} +} // namespace v8::internal diff --git a/src/runtime/runtime-compiler.cc b/src/runtime/runtime-compiler.cc new file mode 100644 index 0000000..3f7e936 --- /dev/null +++ b/src/runtime/runtime-compiler.cc @@ -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; + 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 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; + 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(type_arg); + Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate); + DCHECK(AllowHeapAllocation::IsAllowed()); + + Handle function = deoptimizer->function(); + Handle 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 function, + Handle 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 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(frame->LookupCode()); + } + + uint32_t pc_offset = + static_cast(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 result = Handle::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 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) { + 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 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(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 error_message = + context->ErrorMessageForCodeGenerationFromStrings(); + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewEvalError("code_gen_from_strings", + HandleVector(&error_message, 1))); + } + + // Compile source string in the native context. + ParseRestriction restriction = function_literal_only + ? ONLY_SINGLE_FUNCTION_LITERAL + : NO_PARSE_RESTRICTION; + Handle fun; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, fun, + Compiler::GetFunctionFromEval(source, context, SLOPPY, restriction, + RelocInfo::kNoPosition)); + return *fun; +} + + +static ObjectPair CompileGlobalEval(Isolate* isolate, Handle source, + Handle receiver, + StrictMode strict_mode, + int scope_position) { + Handle context = Handle(isolate->context()); + Handle native_context = Handle(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 error_message = + native_context->ErrorMessageForCodeGenerationFromStrings(); + Handle error; + MaybeHandle maybe_error = isolate->factory()->NewEvalError( + "code_gen_from_strings", HandleVector(&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 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 callee = args.at(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(args.smi_at(3)); + DCHECK(args[4]->IsSmi()); + return CompileGlobalEval(isolate, args.at(1), args.at(2), + strict_mode, args.smi_at(4)); +} +} +} // namespace v8::internal diff --git a/src/runtime/runtime-maths.cc b/src/runtime/runtime-maths.cc new file mode 100644 index 0000000..16acb39 --- /dev/null +++ b/src/runtime/runtime-maths.cc @@ -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(integer)); +} + + +RUNTIME_FUNCTION(Runtime_DoubleLo) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_DOUBLE_ARG_CHECKED(x, 0); + return *isolate->factory()->NewNumber( + static_cast(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(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 array = factory->NewFixedArray(3); + Handle y0 = factory->NewHeapNumber(y[0]); + Handle 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 number = Handle::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(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 diff --git a/src/runtime/runtime-numbers.cc b/src/runtime/runtime-numbers.cc new file mode 100644 index 0000000..3286aa6 --- /dev/null +++ b/src/runtime/runtime-numbers.cc @@ -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 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 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 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 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::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(subject->hash_field()), + static_cast(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(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 diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc index 8d245a5..82174e9 100644 --- a/src/runtime/runtime-strings.cc +++ b/src/runtime/runtime-strings.cc @@ -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); diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc new file mode 100644 index 0000000..eac3c61 --- /dev/null +++ b/src/runtime/runtime-test.cc @@ -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 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(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 diff --git a/src/runtime/runtime-typedarray.cc b/src/runtime/runtime-typedarray.cc new file mode 100644 index 0000000..c138a4f --- /dev/null +++ b/src/runtime/runtime-typedarray.cc @@ -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(isolate) + ->AdjustAmountOfExternalAllocatedMemory( + -static_cast(allocated_length)); + CHECK(V8::ArrayBufferAllocator() != NULL); + V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(), + allocated_length); +} + + +void Runtime::SetupArrayBuffer(Isolate* isolate, + Handle 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 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 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(isolate) + ->AdjustAmountOfExternalAllocatedMemory(allocated_length); + + return true; +} + + +void Runtime::NeuterArrayBuffer(Handle array_buffer) { + Isolate* isolate = array_buffer->GetIsolate(); + for (Handle view_obj(array_buffer->weak_first_view(), isolate); + !view_obj->IsUndefined();) { + Handle 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(NULL, 0))); + } + if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder, + allocated_length)) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewRangeError("invalid_array_buffer_length", + HandleVector(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(source->backing_store()); + uint8_t* target_data = reinterpret_cast(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 buffer = Handle::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(Smi::kMaxValue)) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewRangeError("invalid_typed_array_length", + HandleVector(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 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 buffer = Handle::cast(maybe_buffer); + holder->set_buffer(*buffer); + holder->set_weak_next(buffer->weak_first_view()); + buffer->set_weak_first_view(*holder); + + Handle elements = isolate->factory()->NewExternalArray( + static_cast(length), array_type, + static_cast(buffer->backing_store()) + byte_offset); + Handle 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 elements = + isolate->factory()->NewFixedTypedArray(static_cast(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 buffer = isolate->factory()->NewJSArrayBuffer(); + if (source->IsJSTypedArray() && + JSTypedArray::cast(*source)->type() == array_type) { + length_obj = Handle(JSTypedArray::cast(*source)->length(), isolate); + } + size_t length = 0; + RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length)); + + if ((length > static_cast(Smi::kMaxValue)) || + (length > (kMaxInt / element_size))) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewRangeError("invalid_typed_array_length", + HandleVector(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(NULL, 0))); + } + + holder->set_buffer(*buffer); + holder->set_byte_offset(Smi::FromInt(0)); + Handle 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 elements = isolate->factory()->NewExternalArray( + static_cast(length), array_type, + static_cast(buffer->backing_store())); + Handle map = + JSObject::GetElementsTransitionMap(holder, external_elements_kind); + JSObject::SetMapAndElements(holder, map, elements); + + if (source->IsJSTypedArray()) { + Handle typed_array(JSTypedArray::cast(*source)); + + if (typed_array->type() == holder->type()) { + uint8_t* backing_store = + static_cast(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(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 target(JSTypedArray::cast(*target_obj)); + Handle 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(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(target->GetBuffer()->backing_store()) + + target_offset; + uint8_t* source_base = + static_cast(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 +inline void CopyBytes(uint8_t* target, uint8_t* source) { + for (int i = 0; i < n; i++) { + *(target++) = *(source++); + } +} + + +template +inline void FlipBytes(uint8_t* target, uint8_t* source) { + source = source + (n - 1); + for (int i = 0; i < n; i++) { + *(target++) = *(source--); + } +} + + +template +inline static bool DataViewGetValue(Isolate* isolate, + Handle data_view, + Handle 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 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(buffer->backing_store()) + buffer_offset; + if (NeedToFlipBytes(is_little_endian)) { + FlipBytes(value.bytes, source); + } else { + CopyBytes(value.bytes, source); + } + *result = value.data; + return true; +} + + +template +static bool DataViewSetValue(Isolate* isolate, Handle data_view, + Handle 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 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(buffer->backing_store()) + buffer_offset; + if (NeedToFlipBytes(is_little_endian)) { + FlipBytes(target, value.bytes); + } else { + CopyBytes(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(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 +static T DataViewConvertValue(double value); + + +template <> +int8_t DataViewConvertValue(double value) { + return static_cast(DoubleToInt32(value)); +} + + +template <> +int16_t DataViewConvertValue(double value) { + return static_cast(DoubleToInt32(value)); +} + + +template <> +int32_t DataViewConvertValue(double value) { + return DoubleToInt32(value); +} + + +template <> +uint8_t DataViewConvertValue(double value) { + return static_cast(DoubleToUint32(value)); +} + + +template <> +uint16_t DataViewConvertValue(double value) { + return static_cast(DoubleToUint32(value)); +} + + +template <> +uint32_t DataViewConvertValue(double value) { + return DoubleToUint32(value); +} + + +template <> +float DataViewConvertValue(double value) { + return static_cast(value); +} + + +template <> +double DataViewConvertValue(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(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(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 diff --git a/src/runtime/runtime.cc b/src/runtime/runtime.cc index 0cafc3e..427b821 100644 --- a/src/runtime/runtime.cc +++ b/src/runtime/runtime.cc @@ -40,15 +40,8 @@ #include "src/utils.h" #include "src/v8threads.h" #include "src/vm-state-inl.h" -#include "third_party/fdlibm/fdlibm.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 { @@ -647,4456 +640,2640 @@ RUNTIME_FUNCTION(Runtime_Fix) { } -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()); +RUNTIME_FUNCTION(Runtime_GetPrototype) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0); + // We don't expect access checks to be needed on JSProxy objects. + DCHECK(!obj->IsAccessCheckNeeded() || obj->IsJSObject()); + PrototypeIterator iter(isolate, obj, PrototypeIterator::START_AT_RECEIVER); + do { + if (PrototypeIterator::GetCurrent(iter)->IsAccessCheckNeeded() && + !isolate->MayNamedAccess( + Handle::cast(PrototypeIterator::GetCurrent(iter)), + isolate->factory()->proto_string(), v8::ACCESS_GET)) { + isolate->ReportFailedAccessCheck( + Handle::cast(PrototypeIterator::GetCurrent(iter)), + v8::ACCESS_GET); + RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); + return isolate->heap()->undefined_value(); + } + iter.AdvanceIgnoringProxies(); + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + return *PrototypeIterator::GetCurrent(iter); + } + } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)); + return *PrototypeIterator::GetCurrent(iter); +} + + +static inline Handle GetPrototypeSkipHiddenPrototypes( + Isolate* isolate, Handle receiver) { + PrototypeIterator iter(isolate, receiver); + while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { + if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { + return PrototypeIterator::GetCurrent(iter); + } + iter.Advance(); } - if (phantom_array_buffer->is_external()) return; + return PrototypeIterator::GetCurrent(iter); +} - size_t allocated_length = - NumberToSize(isolate, phantom_array_buffer->byte_length()); - reinterpret_cast(isolate) - ->AdjustAmountOfExternalAllocatedMemory( - -static_cast(allocated_length)); - CHECK(V8::ArrayBufferAllocator() != NULL); - V8::ArrayBufferAllocator()->Free(phantom_array_buffer->backing_store(), - allocated_length); +RUNTIME_FUNCTION(Runtime_InternalSetPrototype) { + HandleScope scope(isolate); + DCHECK(args.length() == 2); + CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); + DCHECK(!obj->IsAccessCheckNeeded()); + DCHECK(!obj->map()->is_observed()); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, JSObject::SetPrototype(obj, prototype, false)); + return *result; } -void Runtime::SetupArrayBuffer(Isolate* isolate, - Handle 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)); +RUNTIME_FUNCTION(Runtime_SetPrototype) { + HandleScope scope(isolate); + DCHECK(args.length() == 2); + CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); + if (obj->IsAccessCheckNeeded() && + !isolate->MayNamedAccess(obj, isolate->factory()->proto_string(), + v8::ACCESS_SET)) { + isolate->ReportFailedAccessCheck(obj, v8::ACCESS_SET); + RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); + return isolate->heap()->undefined_value(); } - array_buffer->set_backing_store(data); - array_buffer->set_flag(Smi::FromInt(0)); - array_buffer->set_is_external(is_external); - - Handle byte_length = - isolate->factory()->NewNumberFromSize(allocated_length); - CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); - array_buffer->set_byte_length(*byte_length); + if (obj->map()->is_observed()) { + Handle old_value = GetPrototypeSkipHiddenPrototypes(isolate, obj); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, JSObject::SetPrototype(obj, prototype, true)); - 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()); + Handle new_value = GetPrototypeSkipHiddenPrototypes(isolate, obj); + if (!new_value->SameValue(*old_value)) { + JSObject::EnqueueChangeRecord( + obj, "setPrototype", isolate->factory()->proto_string(), old_value); + } + return *result; + } + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, JSObject::SetPrototype(obj, prototype, true)); + return *result; } -bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate, - Handle 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; +RUNTIME_FUNCTION(Runtime_IsInPrototypeChain) { + HandleScope shs(isolate); + DCHECK(args.length() == 2); + // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8). + CONVERT_ARG_HANDLE_CHECKED(Object, O, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, V, 1); + PrototypeIterator iter(isolate, V, PrototypeIterator::START_AT_RECEIVER); + while (true) { + iter.AdvanceIgnoringProxies(); + if (iter.IsAtEnd()) return isolate->heap()->false_value(); + if (iter.IsAtEnd(O)) return isolate->heap()->true_value(); } +} + - SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length); +// Enumerator used as indices into the array returned from GetOwnProperty +enum PropertyDescriptorIndices { + IS_ACCESSOR_INDEX, + VALUE_INDEX, + GETTER_INDEX, + SETTER_INDEX, + WRITABLE_INDEX, + ENUMERABLE_INDEX, + CONFIGURABLE_INDEX, + DESCRIPTOR_SIZE +}; - reinterpret_cast(isolate) - ->AdjustAmountOfExternalAllocatedMemory(allocated_length); - return true; -} +MUST_USE_RESULT static MaybeHandle GetOwnProperty(Isolate* isolate, + Handle obj, + Handle name) { + Heap* heap = isolate->heap(); + Factory* factory = isolate->factory(); + PropertyAttributes attrs; + uint32_t index = 0; + Handle value; + MaybeHandle maybe_accessors; + // TODO(verwaest): Unify once indexed properties can be handled by the + // LookupIterator. + if (name->AsArrayIndex(&index)) { + // Get attributes. + Maybe maybe = + JSReceiver::GetOwnElementAttribute(obj, index); + if (!maybe.has_value) return MaybeHandle(); + attrs = maybe.value; + if (attrs == ABSENT) return factory->undefined_value(); -void Runtime::NeuterArrayBuffer(Handle array_buffer) { - Isolate* isolate = array_buffer->GetIsolate(); - for (Handle view_obj(array_buffer->weak_first_view(), isolate); - !view_obj->IsUndefined();) { - Handle view(JSArrayBufferView::cast(*view_obj)); - if (view->IsJSTypedArray()) { - JSTypedArray::cast(*view)->Neuter(); - } else if (view->IsJSDataView()) { - JSDataView::cast(*view)->Neuter(); - } else { - UNREACHABLE(); + // Get AccessorPair if present. + maybe_accessors = JSObject::GetOwnElementAccessorPair(obj, index); + + // Get value if not an AccessorPair. + if (maybe_accessors.is_null()) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, value, Runtime::GetElementOrCharAt(isolate, obj, index), + Object); } - view_obj = handle(view->weak_next(), isolate); - } - array_buffer->Neuter(); -} + } else { + // Get attributes. + LookupIterator it(obj, name, LookupIterator::HIDDEN); + Maybe maybe = JSObject::GetPropertyAttributes(&it); + if (!maybe.has_value) return MaybeHandle(); + attrs = maybe.value; + if (attrs == ABSENT) return factory->undefined_value(); + // Get AccessorPair if present. + if (it.state() == LookupIterator::ACCESSOR && + it.GetAccessors()->IsAccessorPair()) { + maybe_accessors = Handle::cast(it.GetAccessors()); + } -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(NULL, 0))); - } - if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder, - allocated_length)) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError("invalid_array_buffer_length", - HandleVector(NULL, 0))); + // Get value if not an AccessorPair. + if (maybe_accessors.is_null()) { + ASSIGN_RETURN_ON_EXCEPTION(isolate, value, Object::GetProperty(&it), + Object); + } } - return *holder; -} + DCHECK(!isolate->has_pending_exception()); + Handle elms = factory->NewFixedArray(DESCRIPTOR_SIZE); + elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0)); + elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0)); + elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(!maybe_accessors.is_null())); + Handle accessors; + if (maybe_accessors.ToHandle(&accessors)) { + Handle getter(accessors->GetComponent(ACCESSOR_GETTER), isolate); + Handle setter(accessors->GetComponent(ACCESSOR_SETTER), isolate); + elms->set(GETTER_INDEX, *getter); + elms->set(SETTER_INDEX, *setter); + } else { + elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0)); + elms->set(VALUE_INDEX, *value); + } -RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) { - SealHandleScope shs(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0); - return holder->byte_length(); + return factory->NewJSArrayWithElements(elms); } -RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) { +// Returns an array with the property description: +// if args[1] is not a property on args[0] +// returns undefined +// if args[1] is a data property on args[0] +// [false, value, Writeable, Enumerable, Configurable] +// if args[1] is an accessor on args[0] +// [true, GetFunction, SetFunction, Enumerable, Configurable] +RUNTIME_FUNCTION(Runtime_GetOwnProperty) { 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(source->backing_store()); - uint8_t* target_data = reinterpret_cast(target->backing_store()); - CopyBytes(target_data, source_data + start, target_length); - return isolate->heap()->undefined_value(); + DCHECK(args.length() == 2); + CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); + CONVERT_ARG_HANDLE_CHECKED(Name, name, 1); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + GetOwnProperty(isolate, obj, name)); + return *result; } -RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) { +RUNTIME_FUNCTION(Runtime_PreventExtensions) { HandleScope scope(isolate); DCHECK(args.length() == 1); - CONVERT_ARG_CHECKED(Object, object, 0); - return isolate->heap()->ToBoolean(object->IsJSArrayBufferView()); + CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + JSObject::PreventExtensions(obj)); + return *result; } -RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) { +RUNTIME_FUNCTION(Runtime_ToMethod) { 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(); + DCHECK(args.length() == 2); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); + Handle clone = JSFunction::CloneClosure(fun); + Handle home_object_symbol(isolate->heap()->home_object_symbol()); + JSObject::SetOwnPropertyIgnoreAttributes(clone, home_object_symbol, + home_object, DONT_ENUM).Assert(); + return *clone; } -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_HomeObjectSymbol) { + DCHECK(args.length() == 0); + return isolate->heap()->home_object_symbol(); } -RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) { +RUNTIME_FUNCTION(Runtime_LoadFromSuper) { 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 buffer = Handle::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()); + DCHECK(args.length() == 3); + CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); + CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); + + if (home_object->IsAccessCheckNeeded() && + !isolate->MayNamedAccess(home_object, name, v8::ACCESS_GET)) { + isolate->ReportFailedAccessCheck(home_object, v8::ACCESS_GET); + RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); } - RUNTIME_ASSERT(byte_length % element_size == 0); - size_t length = byte_length / element_size; + PrototypeIterator iter(isolate, home_object); + Handle proto = PrototypeIterator::GetCurrent(iter); + if (!proto->IsJSReceiver()) return isolate->heap()->undefined_value(); - if (length > static_cast(Smi::kMaxValue)) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError("invalid_typed_array_length", - HandleVector(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 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 buffer = Handle::cast(maybe_buffer); - holder->set_buffer(*buffer); - holder->set_weak_next(buffer->weak_first_view()); - buffer->set_weak_first_view(*holder); - - Handle elements = isolate->factory()->NewExternalArray( - static_cast(length), array_type, - static_cast(buffer->backing_store()) + byte_offset); - Handle 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 elements = - isolate->factory()->NewFixedTypedArray(static_cast(length), - array_type); - holder->set_elements(*elements); - } - return isolate->heap()->undefined_value(); + LookupIterator it(receiver, name, Handle::cast(proto)); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Object::GetProperty(&it)); + return *result; } -// 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 buffer = isolate->factory()->NewJSArrayBuffer(); - if (source->IsJSTypedArray() && - JSTypedArray::cast(*source)->type() == array_type) { - length_obj = Handle(JSTypedArray::cast(*source)->length(), isolate); - } - size_t length = 0; - RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length)); - - if ((length > static_cast(Smi::kMaxValue)) || - (length > (kMaxInt / element_size))) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError("invalid_typed_array_length", - HandleVector(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(NULL, 0))); - } - - holder->set_buffer(*buffer); - holder->set_byte_offset(Smi::FromInt(0)); - Handle 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 elements = isolate->factory()->NewExternalArray( - static_cast(length), array_type, - static_cast(buffer->backing_store())); - Handle map = - JSObject::GetElementsTransitionMap(holder, external_elements_kind); - JSObject::SetMapAndElements(holder, map, elements); - - if (source->IsJSTypedArray()) { - Handle typed_array(JSTypedArray::cast(*source)); - - if (typed_array->type() == holder->type()) { - uint8_t* backing_store = - static_cast(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(); \ +static Object* StoreToSuper(Isolate* isolate, Handle home_object, + Handle receiver, Handle name, + Handle value, StrictMode strict_mode) { + if (home_object->IsAccessCheckNeeded() && + !isolate->MayNamedAccess(home_object, name, v8::ACCESS_SET)) { + isolate->ReportFailedAccessCheck(home_object, v8::ACCESS_SET); + RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); } -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 + PrototypeIterator iter(isolate, home_object); + Handle proto = PrototypeIterator::GetCurrent(iter); + if (!proto->IsJSReceiver()) return isolate->heap()->undefined_value(); -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 -}; + LookupIterator it(receiver, name, Handle::cast(proto)); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + Object::SetProperty(&it, value, strict_mode, + Object::CERTAINLY_NOT_STORE_FROM_KEYED, + Object::SUPER_PROPERTY)); + return *result; +} -RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) { +RUNTIME_FUNCTION(Runtime_StoreToSuper_Strict) { HandleScope scope(isolate); - DCHECK(args.length() == 3); - if (!args[0]->IsJSTypedArray()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewTypeError("not_typed_array", HandleVector(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 target(JSTypedArray::cast(*target_obj)); - Handle 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(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(target->GetBuffer()->backing_store()) + - target_offset; - uint8_t* source_base = - static_cast(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); - } -} - + DCHECK(args.length() == 4); + CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); + CONVERT_ARG_HANDLE_CHECKED(Name, name, 3); -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); + return StoreToSuper(isolate, home_object, receiver, name, value, STRICT); } -RUNTIME_FUNCTION(Runtime_DataViewInitialize) { +RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) { 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(); -} - + CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); + CONVERT_ARG_HANDLE_CHECKED(Name, name, 3); -inline static bool NeedToFlipBytes(bool is_little_endian) { -#ifdef V8_TARGET_LITTLE_ENDIAN - return !is_little_endian; -#else - return is_little_endian; -#endif + return StoreToSuper(isolate, home_object, receiver, name, value, SLOPPY); } -template -inline void CopyBytes(uint8_t* target, uint8_t* source) { - for (int i = 0; i < n; i++) { - *(target++) = *(source++); +RUNTIME_FUNCTION(Runtime_IsExtensible) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSObject, obj, 0); + if (obj->IsJSGlobalProxy()) { + PrototypeIterator iter(isolate, obj); + if (iter.IsAtEnd()) return isolate->heap()->false_value(); + DCHECK(iter.GetCurrent()->IsJSGlobalObject()); + obj = JSObject::cast(iter.GetCurrent()); } + return isolate->heap()->ToBoolean(obj->map()->is_extensible()); } -template -inline void FlipBytes(uint8_t* target, uint8_t* source) { - source = source + (n - 1); - for (int i = 0; i < n; i++) { - *(target++) = *(source--); - } +RUNTIME_FUNCTION(Runtime_CreateApiFunction) { + HandleScope scope(isolate); + DCHECK(args.length() == 2); + CONVERT_ARG_HANDLE_CHECKED(FunctionTemplateInfo, data, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); + return *isolate->factory()->CreateApiFunction(data, prototype); } -template -inline static bool DataViewGetValue(Isolate* isolate, - Handle data_view, - Handle 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 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(buffer->backing_store()) + buffer_offset; - if (NeedToFlipBytes(is_little_endian)) { - FlipBytes(value.bytes, source); - } else { - CopyBytes(value.bytes, source); - } - *result = value.data; - return true; +RUNTIME_FUNCTION(Runtime_IsTemplate) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(Object, arg, 0); + bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo(); + return isolate->heap()->ToBoolean(result); } -template -static bool DataViewSetValue(Isolate* isolate, Handle data_view, - Handle 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 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(buffer->backing_store()) + buffer_offset; - if (NeedToFlipBytes(is_little_endian)) { - FlipBytes(target, value.bytes); +RUNTIME_FUNCTION(Runtime_GetTemplateField) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 2); + CONVERT_ARG_CHECKED(HeapObject, templ, 0); + CONVERT_SMI_ARG_CHECKED(index, 1); + int offset = index * kPointerSize + HeapObject::kHeaderSize; + InstanceType type = templ->map()->instance_type(); + RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE || + type == OBJECT_TEMPLATE_INFO_TYPE); + RUNTIME_ASSERT(offset > 0); + if (type == FUNCTION_TEMPLATE_INFO_TYPE) { + RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize); } else { - CopyBytes(target, value.bytes); + RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize); } - return true; + return *HeapObject::RawField(templ, offset); } -#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(NULL, 0))); \ - } \ +RUNTIME_FUNCTION(Runtime_DisableAccessChecks) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(HeapObject, object, 0); + Handle old_map(object->map()); + bool needs_access_checks = old_map->is_access_check_needed(); + if (needs_access_checks) { + // Copy map so it won't interfere constructor's initial map. + Handle new_map = Map::Copy(old_map); + new_map->set_is_access_check_needed(false); + JSObject::MigrateToMap(Handle::cast(object), new_map); } - -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 -static T DataViewConvertValue(double value); - - -template <> -int8_t DataViewConvertValue(double value) { - return static_cast(DoubleToInt32(value)); + return isolate->heap()->ToBoolean(needs_access_checks); } -template <> -int16_t DataViewConvertValue(double value) { - return static_cast(DoubleToInt32(value)); +RUNTIME_FUNCTION(Runtime_EnableAccessChecks) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); + Handle old_map(object->map()); + RUNTIME_ASSERT(!old_map->is_access_check_needed()); + // Copy map so it won't interfere constructor's initial map. + Handle new_map = Map::Copy(old_map); + new_map->set_is_access_check_needed(true); + JSObject::MigrateToMap(object, new_map); + return isolate->heap()->undefined_value(); } -template <> -int32_t DataViewConvertValue(double value) { - return DoubleToInt32(value); +static Object* ThrowRedeclarationError(Isolate* isolate, Handle name) { + HandleScope scope(isolate); + Handle args[1] = {name}; + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError("var_redeclaration", HandleVector(args, 1))); } -template <> -uint8_t DataViewConvertValue(double value) { - return static_cast(DoubleToUint32(value)); -} +// May throw a RedeclarationError. +static Object* DeclareGlobals(Isolate* isolate, Handle global, + Handle name, Handle value, + PropertyAttributes attr, bool is_var, + bool is_const, bool is_function) { + // Do the lookup own properties only, see ES5 erratum. + LookupIterator it(global, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); + Maybe maybe = JSReceiver::GetPropertyAttributes(&it); + if (!maybe.has_value) return isolate->heap()->exception(); + if (it.IsFound()) { + PropertyAttributes old_attributes = maybe.value; + // The name was declared before; check for conflicting re-declarations. + if (is_const) return ThrowRedeclarationError(isolate, name); -template <> -uint16_t DataViewConvertValue(double value) { - return static_cast(DoubleToUint32(value)); -} + // Skip var re-declarations. + if (is_var) return isolate->heap()->undefined_value(); + DCHECK(is_function); + if ((old_attributes & DONT_DELETE) != 0) { + // Only allow reconfiguring globals to functions in user code (no + // natives, which are marked as read-only). + DCHECK((attr & READ_ONLY) == 0); -template <> -uint32_t DataViewConvertValue(double value) { - return DoubleToUint32(value); -} + // Check whether we can reconfigure the existing property into a + // function. + PropertyDetails old_details = it.property_details(); + // TODO(verwaest): CALLBACKS invalidly includes ExecutableAccessInfo, + // which are actually data properties, not accessor properties. + if (old_details.IsReadOnly() || old_details.IsDontEnum() || + old_details.type() == CALLBACKS) { + return ThrowRedeclarationError(isolate, name); + } + // If the existing property is not configurable, keep its attributes. Do + attr = old_attributes; + } + } + // Define or redefine own property. + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes( + global, name, value, attr)); -template <> -float DataViewConvertValue(double value) { - return static_cast(value); + return isolate->heap()->undefined_value(); } -template <> -double DataViewConvertValue(double value) { - return value; -} - +RUNTIME_FUNCTION(Runtime_DeclareGlobals) { + HandleScope scope(isolate); + DCHECK(args.length() == 3); + Handle global(isolate->global_object()); -#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(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(NULL, 0))); \ - } \ - } + CONVERT_ARG_HANDLE_CHECKED(Context, context, 0); + CONVERT_ARG_HANDLE_CHECKED(FixedArray, pairs, 1); + CONVERT_SMI_ARG_CHECKED(flags, 2); -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) + // Traverse the name/value pairs and set the properties. + int length = pairs->length(); + for (int i = 0; i < length; i += 2) { + HandleScope scope(isolate); + Handle name(String::cast(pairs->get(i))); + Handle initial_value(pairs->get(i + 1), isolate); -#undef DATA_VIEW_SETTER + // We have to declare a global const property. To capture we only + // assign to it when evaluating the assignment for "const x = + // " the initial value is the hole. + bool is_var = initial_value->IsUndefined(); + bool is_const = initial_value->IsTheHole(); + bool is_function = initial_value->IsSharedFunctionInfo(); + DCHECK(is_var + is_const + is_function == 1); + Handle value; + if (is_function) { + // Copy the function and update its context. Use it as value. + Handle shared = + Handle::cast(initial_value); + Handle function = + isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context, + TENURED); + value = function; + } else { + value = isolate->factory()->undefined_value(); + } -RUNTIME_FUNCTION(Runtime_SetInitialize) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - Handle table = isolate->factory()->NewOrderedHashSet(); - holder->set_table(*table); - return *holder; -} + // Compute the property attributes. According to ECMA-262, + // the property must be non-configurable except in eval. + bool is_native = DeclareGlobalsNativeFlag::decode(flags); + bool is_eval = DeclareGlobalsEvalFlag::decode(flags); + int attr = NONE; + if (is_const) attr |= READ_ONLY; + if (is_function && is_native) attr |= READ_ONLY; + if (!is_const && !is_eval) attr |= DONT_DELETE; + Object* result = DeclareGlobals(isolate, global, name, value, + static_cast(attr), + is_var, is_const, is_function); + if (isolate->has_pending_exception()) return result; + } -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 table(OrderedHashSet::cast(holder->table())); - table = OrderedHashSet::Add(table, key); - holder->set_table(*table); - return *holder; + return isolate->heap()->undefined_value(); } -RUNTIME_FUNCTION(Runtime_SetHas) { +RUNTIME_FUNCTION(Runtime_InitializeVarGlobal) { HandleScope scope(isolate); - DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(OrderedHashSet::cast(holder->table())); - return isolate->heap()->ToBoolean(table->Contains(key)); -} - + // args[0] == name + // args[1] == language_mode + // args[2] == value (optional) -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 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); -} + // Determine if we need to assign to the variable if it already + // exists (based on the number of arguments). + RUNTIME_ASSERT(args.length() == 3); + CONVERT_ARG_HANDLE_CHECKED(String, name, 0); + CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); -RUNTIME_FUNCTION(Runtime_SetClear) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - Handle table(OrderedHashSet::cast(holder->table())); - table = OrderedHashSet::Clear(table); - holder->set_table(*table); - return isolate->heap()->undefined_value(); + Handle global(isolate->context()->global_object()); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, Object::SetProperty(global, name, value, strict_mode)); + return *result; } -RUNTIME_FUNCTION(Runtime_SetGetSize) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - Handle table(OrderedHashSet::cast(holder->table())); - return Smi::FromInt(table->NumberOfElements()); -} +RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) { + HandleScope handle_scope(isolate); + // All constants are declared with an initial value. The name + // of the constant is the first argument and the initial value + // is the second. + RUNTIME_ASSERT(args.length() == 2); + CONVERT_ARG_HANDLE_CHECKED(String, name, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, value, 1); + Handle global = isolate->global_object(); -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 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(); -} + // Lookup the property as own on the global object. + LookupIterator it(global, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); + Maybe maybe = JSReceiver::GetPropertyAttributes(&it); + DCHECK(maybe.has_value); + PropertyAttributes old_attributes = maybe.value; + + PropertyAttributes attr = + static_cast(DONT_DELETE | READ_ONLY); + // Set the value if the property is either missing, or the property attributes + // allow setting the value without invoking an accessor. + if (it.IsFound()) { + // Ignore if we can't reconfigure the value. + if ((old_attributes & DONT_DELETE) != 0) { + if ((old_attributes & READ_ONLY) != 0 || + it.state() == LookupIterator::ACCESSOR) { + return *value; + } + attr = static_cast(old_attributes | READ_ONLY); + } + } + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes( + global, name, value, attr)); -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); + return *value; } -RUNTIME_FUNCTION(Runtime_MapInitialize) { +RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) { HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - Handle table = isolate->factory()->NewOrderedHashMap(); - holder->set_table(*table); - return *holder; -} + DCHECK(args.length() == 4); + // Declarations are always made in a function, native, or global context. In + // the case of eval code, the context passed is the context of the caller, + // which may be some nested context and not the declaration context. + CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0); + Handle context(context_arg->declaration_context()); + CONVERT_ARG_HANDLE_CHECKED(String, name, 1); + CONVERT_SMI_ARG_CHECKED(attr_arg, 2); + PropertyAttributes attr = static_cast(attr_arg); + RUNTIME_ASSERT(attr == READ_ONLY || attr == NONE); + CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3); -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 table(OrderedHashMap::cast(holder->table())); - Handle lookup(table->Lookup(key), isolate); - return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup; -} + // TODO(verwaest): Unify the encoding indicating "var" with DeclareGlobals. + bool is_var = *initial_value == NULL; + bool is_const = initial_value->IsTheHole(); + bool is_function = initial_value->IsJSFunction(); + DCHECK(is_var + is_const + is_function == 1); + int index; + PropertyAttributes attributes; + ContextLookupFlags flags = DONT_FOLLOW_CHAINS; + BindingFlags binding_flags; + Handle holder = + context->Lookup(name, flags, &index, &attributes, &binding_flags); -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 table(OrderedHashMap::cast(holder->table())); - Handle lookup(table->Lookup(key), isolate); - return isolate->heap()->ToBoolean(!lookup->IsTheHole()); -} + Handle object; + Handle value = + is_function ? initial_value + : Handle::cast(isolate->factory()->undefined_value()); + // TODO(verwaest): This case should probably not be covered by this function, + // but by DeclareGlobals instead. + if ((attributes != ABSENT && holder->IsJSGlobalObject()) || + (context_arg->has_extension() && + context_arg->extension()->IsJSGlobalObject())) { + return DeclareGlobals(isolate, Handle::cast(holder), name, + value, attr, is_var, is_const, is_function); + } -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 table(OrderedHashMap::cast(holder->table())); - bool was_present = false; - Handle new_table = - OrderedHashMap::Remove(table, key, &was_present); - holder->set_table(*new_table); - return isolate->heap()->ToBoolean(was_present); -} + if (attributes != ABSENT) { + // The name was declared before; check for conflicting re-declarations. + if (is_const || (attributes & READ_ONLY) != 0) { + return ThrowRedeclarationError(isolate, name); + } + // Skip var re-declarations. + if (is_var) return isolate->heap()->undefined_value(); -RUNTIME_FUNCTION(Runtime_MapClear) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - Handle table(OrderedHashMap::cast(holder->table())); - table = OrderedHashMap::Clear(table); - holder->set_table(*table); - return isolate->heap()->undefined_value(); -} + DCHECK(is_function); + if (index >= 0) { + DCHECK(holder.is_identical_to(context)); + context->set(index, *initial_value); + return isolate->heap()->undefined_value(); + } + object = Handle::cast(holder); -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 table(OrderedHashMap::cast(holder->table())); - Handle new_table = OrderedHashMap::Put(table, key, value); - holder->set_table(*new_table); - return *holder; -} + } else if (context->has_extension()) { + object = handle(JSObject::cast(context->extension())); + DCHECK(object->IsJSContextExtensionObject() || object->IsJSGlobalObject()); + } else { + DCHECK(context->IsFunctionContext()); + object = + isolate->factory()->NewJSObject(isolate->context_extension_function()); + context->set_extension(*object); + } + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes( + object, name, value, attr)); -RUNTIME_FUNCTION(Runtime_MapGetSize) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - Handle table(OrderedHashMap::cast(holder->table())); - return Smi::FromInt(table->NumberOfElements()); + return isolate->heap()->undefined_value(); } -RUNTIME_FUNCTION(Runtime_MapIteratorInitialize) { +RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) { 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 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(); -} + CONVERT_ARG_HANDLE_CHECKED(Object, value, 0); + DCHECK(!value->IsTheHole()); + // Initializations are always done in a function or native context. + CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 1); + Handle context(context_arg->declaration_context()); + CONVERT_ARG_HANDLE_CHECKED(String, name, 2); + + int index; + PropertyAttributes attributes; + ContextLookupFlags flags = DONT_FOLLOW_CHAINS; + BindingFlags binding_flags; + Handle holder = + context->Lookup(name, flags, &index, &attributes, &binding_flags); -RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0); - Handle table(ObjectHashTable::cast(holder->table())); - Handle 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 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); + if (index >= 0) { + DCHECK(holder->IsContext()); + // Property was found in a context. Perform the assignment if the constant + // was uninitialized. + Handle context = Handle::cast(holder); + DCHECK((attributes & READ_ONLY) != 0); + if (context->get(index)->IsTheHole()) context->set(index, *value); + return *value; } - return *isolate->factory()->NewJSArrayWithElements(entries); -} + PropertyAttributes attr = + static_cast(DONT_DELETE | READ_ONLY); + + // Strict mode handling not needed (legacy const is disallowed in strict + // mode). -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); -} + // The declared const was configurable, and may have been deleted in the + // meanwhile. If so, re-introduce the variable in the context extension. + DCHECK(context_arg->has_extension()); + if (attributes == ABSENT) { + holder = handle(context_arg->extension(), isolate); + } else { + // For JSContextExtensionObjects, the initializer can be run multiple times + // if in a for loop: for (var i = 0; i < 2; i++) { const x = i; }. Only the + // first assignment should go through. For JSGlobalObjects, additionally any + // code can run in between that modifies the declared property. + DCHECK(holder->IsJSGlobalObject() || holder->IsJSContextExtensionObject()); + LookupIterator it(holder, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); + Maybe maybe = JSReceiver::GetPropertyAttributes(&it); + if (!maybe.has_value) return isolate->heap()->exception(); + PropertyAttributes old_attributes = maybe.value; -static Handle WeakCollectionInitialize( - Isolate* isolate, Handle weak_collection) { - DCHECK(weak_collection->map()->inobject_properties() == 0); - Handle table = ObjectHashTable::New(isolate, 0); - weak_collection->set_table(*table); - return weak_collection; -} + // Ignore if we can't reconfigure the value. + if ((old_attributes & DONT_DELETE) != 0) { + if ((old_attributes & READ_ONLY) != 0 || + it.state() == LookupIterator::ACCESSOR) { + return *value; + } + attr = static_cast(old_attributes | READ_ONLY); + } + } + RETURN_FAILURE_ON_EXCEPTION( + isolate, JSObject::SetOwnPropertyIgnoreAttributes( + Handle::cast(holder), name, value, attr)); -RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); - return *WeakCollectionInitialize(isolate, weak_collection); + return *value; } -RUNTIME_FUNCTION(Runtime_WeakCollectionGet) { +RUNTIME_FUNCTION(Runtime_OptimizeObjectForAddingMultipleProperties) { 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 table( - ObjectHashTable::cast(weak_collection->table())); - RUNTIME_ASSERT(table->IsKey(*key)); - Handle lookup(table->Lookup(key), isolate); - return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup; + CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); + CONVERT_SMI_ARG_CHECKED(properties, 1); + // Conservative upper limit to prevent fuzz tests from going OOM. + RUNTIME_ASSERT(properties <= 100000); + if (object->HasFastProperties() && !object->IsJSGlobalProxy()) { + JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); + } + return *object; } -RUNTIME_FUNCTION(Runtime_WeakCollectionHas) { +RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) { 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 table( - ObjectHashTable::cast(weak_collection->table())); - RUNTIME_ASSERT(table->IsKey(*key)); - Handle lookup(table->Lookup(key), isolate); - return isolate->heap()->ToBoolean(!lookup->IsTheHole()); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0); + Object* length = prototype->length(); + RUNTIME_ASSERT(length->IsSmi() && Smi::cast(length)->value() == 0); + RUNTIME_ASSERT(prototype->HasFastSmiOrObjectElements()); + // This is necessary to enable fast checks for absence of elements + // on Array.prototype and below. + prototype->set_elements(isolate->heap()->empty_fixed_array()); + return Smi::FromInt(0); } -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 table( - ObjectHashTable::cast(weak_collection->table())); - RUNTIME_ASSERT(table->IsKey(*key)); - bool was_present = false; - Handle new_table = - ObjectHashTable::Remove(table, key, &was_present); - weak_collection->set_table(*new_table); - return isolate->heap()->ToBoolean(was_present); +static void InstallBuiltin(Isolate* isolate, Handle holder, + const char* name, Builtins::Name builtin_name) { + Handle key = isolate->factory()->InternalizeUtf8String(name); + Handle code(isolate->builtins()->builtin(builtin_name)); + Handle optimized = + isolate->factory()->NewFunctionWithoutPrototype(key, code); + optimized->shared()->DontAdaptArguments(); + JSObject::AddProperty(holder, key, optimized, NONE); } -RUNTIME_FUNCTION(Runtime_WeakCollectionSet) { +RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) { 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 table( - ObjectHashTable::cast(weak_collection->table())); - RUNTIME_ASSERT(table->IsKey(*key)); - Handle new_table = ObjectHashTable::Put(table, key, value); - weak_collection->set_table(*new_table); - return *weak_collection; + DCHECK(args.length() == 0); + Handle holder = + isolate->factory()->NewJSObject(isolate->object_function()); + + InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop); + InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush); + InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift); + InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift); + InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice); + InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice); + InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat); + + return *holder; } -RUNTIME_FUNCTION(Runtime_GetWeakSetValues) { - HandleScope scope(isolate); +RUNTIME_FUNCTION(Runtime_IsSloppyModeFunction) { + SealHandleScope shs(isolate); DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0); - Handle table(ObjectHashTable::cast(holder->table())); - Handle 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 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); + CONVERT_ARG_CHECKED(JSReceiver, callable, 0); + if (!callable->IsJSFunction()) { + HandleScope scope(isolate); + Handle delegate; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, delegate, Execution::TryGetFunctionDelegate( + isolate, Handle(callable))); + callable = JSFunction::cast(*delegate); } - return *isolate->factory()->NewJSArrayWithElements(values); + JSFunction* function = JSFunction::cast(callable); + SharedFunctionInfo* shared = function->shared(); + return isolate->heap()->ToBoolean(shared->strict_mode() == SLOPPY); } -RUNTIME_FUNCTION(Runtime_GetPrototype) { - HandleScope scope(isolate); +RUNTIME_FUNCTION(Runtime_GetDefaultReceiver) { + SealHandleScope shs(isolate); DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0); - // We don't expect access checks to be needed on JSProxy objects. - DCHECK(!obj->IsAccessCheckNeeded() || obj->IsJSObject()); - PrototypeIterator iter(isolate, obj, PrototypeIterator::START_AT_RECEIVER); - do { - if (PrototypeIterator::GetCurrent(iter)->IsAccessCheckNeeded() && - !isolate->MayNamedAccess( - Handle::cast(PrototypeIterator::GetCurrent(iter)), - isolate->factory()->proto_string(), v8::ACCESS_GET)) { - isolate->ReportFailedAccessCheck( - Handle::cast(PrototypeIterator::GetCurrent(iter)), - v8::ACCESS_GET); - RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); - return isolate->heap()->undefined_value(); - } - iter.AdvanceIgnoringProxies(); - if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { - return *PrototypeIterator::GetCurrent(iter); - } - } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)); - return *PrototypeIterator::GetCurrent(iter); -} + CONVERT_ARG_CHECKED(JSReceiver, callable, 0); + if (!callable->IsJSFunction()) { + HandleScope scope(isolate); + Handle delegate; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, delegate, Execution::TryGetFunctionDelegate( + isolate, Handle(callable))); + callable = JSFunction::cast(*delegate); + } + JSFunction* function = JSFunction::cast(callable); -static inline Handle GetPrototypeSkipHiddenPrototypes( - Isolate* isolate, Handle receiver) { - PrototypeIterator iter(isolate, receiver); - while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { - if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { - return PrototypeIterator::GetCurrent(iter); - } - iter.Advance(); + SharedFunctionInfo* shared = function->shared(); + if (shared->native() || shared->strict_mode() == STRICT) { + return isolate->heap()->undefined_value(); } - return PrototypeIterator::GetCurrent(iter); + // Returns undefined for strict or native functions, or + // the associated global receiver for "normal" functions. + + return function->global_proxy(); } -RUNTIME_FUNCTION(Runtime_InternalSetPrototype) { - HandleScope scope(isolate); - DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); - DCHECK(!obj->IsAccessCheckNeeded()); - DCHECK(!obj->map()->is_observed()); - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, JSObject::SetPrototype(obj, prototype, false)); - return *result; +RUNTIME_FUNCTION(Runtime_FunctionGetName) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + + CONVERT_ARG_CHECKED(JSFunction, f, 0); + return f->shared()->name(); } -RUNTIME_FUNCTION(Runtime_SetPrototype) { - HandleScope scope(isolate); +RUNTIME_FUNCTION(Runtime_FunctionSetName) { + SealHandleScope shs(isolate); DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); - if (obj->IsAccessCheckNeeded() && - !isolate->MayNamedAccess(obj, isolate->factory()->proto_string(), - v8::ACCESS_SET)) { - isolate->ReportFailedAccessCheck(obj, v8::ACCESS_SET); - RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); - return isolate->heap()->undefined_value(); - } - if (obj->map()->is_observed()) { - Handle old_value = GetPrototypeSkipHiddenPrototypes(isolate, obj); - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, JSObject::SetPrototype(obj, prototype, true)); - Handle new_value = GetPrototypeSkipHiddenPrototypes(isolate, obj); - if (!new_value->SameValue(*old_value)) { - JSObject::EnqueueChangeRecord( - obj, "setPrototype", isolate->factory()->proto_string(), old_value); - } - return *result; - } - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, JSObject::SetPrototype(obj, prototype, true)); - return *result; + CONVERT_ARG_CHECKED(JSFunction, f, 0); + CONVERT_ARG_CHECKED(String, name, 1); + f->shared()->set_name(name); + return isolate->heap()->undefined_value(); } -RUNTIME_FUNCTION(Runtime_IsInPrototypeChain) { - HandleScope shs(isolate); - DCHECK(args.length() == 2); - // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8). - CONVERT_ARG_HANDLE_CHECKED(Object, O, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, V, 1); - PrototypeIterator iter(isolate, V, PrototypeIterator::START_AT_RECEIVER); - while (true) { - iter.AdvanceIgnoringProxies(); - if (iter.IsAtEnd()) return isolate->heap()->false_value(); - if (iter.IsAtEnd(O)) return isolate->heap()->true_value(); - } +RUNTIME_FUNCTION(Runtime_FunctionNameShouldPrintAsAnonymous) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSFunction, f, 0); + return isolate->heap()->ToBoolean( + f->shared()->name_should_print_as_anonymous()); } -// Enumerator used as indices into the array returned from GetOwnProperty -enum PropertyDescriptorIndices { - IS_ACCESSOR_INDEX, - VALUE_INDEX, - GETTER_INDEX, - SETTER_INDEX, - WRITABLE_INDEX, - ENUMERABLE_INDEX, - CONFIGURABLE_INDEX, - DESCRIPTOR_SIZE -}; +RUNTIME_FUNCTION(Runtime_FunctionMarkNameShouldPrintAsAnonymous) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSFunction, f, 0); + f->shared()->set_name_should_print_as_anonymous(true); + return isolate->heap()->undefined_value(); +} -MUST_USE_RESULT static MaybeHandle GetOwnProperty(Isolate* isolate, - Handle obj, - Handle name) { - Heap* heap = isolate->heap(); - Factory* factory = isolate->factory(); +RUNTIME_FUNCTION(Runtime_FunctionIsGenerator) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSFunction, f, 0); + return isolate->heap()->ToBoolean(f->shared()->is_generator()); +} - PropertyAttributes attrs; - uint32_t index = 0; - Handle value; - MaybeHandle maybe_accessors; - // TODO(verwaest): Unify once indexed properties can be handled by the - // LookupIterator. - if (name->AsArrayIndex(&index)) { - // Get attributes. - Maybe maybe = - JSReceiver::GetOwnElementAttribute(obj, index); - if (!maybe.has_value) return MaybeHandle(); - attrs = maybe.value; - if (attrs == ABSENT) return factory->undefined_value(); - // Get AccessorPair if present. - maybe_accessors = JSObject::GetOwnElementAccessorPair(obj, index); +RUNTIME_FUNCTION(Runtime_FunctionIsArrow) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSFunction, f, 0); + return isolate->heap()->ToBoolean(f->shared()->is_arrow()); +} - // Get value if not an AccessorPair. - if (maybe_accessors.is_null()) { - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, Runtime::GetElementOrCharAt(isolate, obj, index), - Object); - } - } else { - // Get attributes. - LookupIterator it(obj, name, LookupIterator::HIDDEN); - Maybe maybe = JSObject::GetPropertyAttributes(&it); - if (!maybe.has_value) return MaybeHandle(); - attrs = maybe.value; - if (attrs == ABSENT) return factory->undefined_value(); - // Get AccessorPair if present. - if (it.state() == LookupIterator::ACCESSOR && - it.GetAccessors()->IsAccessorPair()) { - maybe_accessors = Handle::cast(it.GetAccessors()); - } +RUNTIME_FUNCTION(Runtime_FunctionIsConciseMethod) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSFunction, f, 0); + return isolate->heap()->ToBoolean(f->shared()->is_concise_method()); +} - // Get value if not an AccessorPair. - if (maybe_accessors.is_null()) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, Object::GetProperty(&it), - Object); - } - } - DCHECK(!isolate->has_pending_exception()); - Handle elms = factory->NewFixedArray(DESCRIPTOR_SIZE); - elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0)); - elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0)); - elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(!maybe_accessors.is_null())); - Handle accessors; - if (maybe_accessors.ToHandle(&accessors)) { - Handle getter(accessors->GetComponent(ACCESSOR_GETTER), isolate); - Handle setter(accessors->GetComponent(ACCESSOR_SETTER), isolate); - elms->set(GETTER_INDEX, *getter); - elms->set(SETTER_INDEX, *setter); - } else { - elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0)); - elms->set(VALUE_INDEX, *value); - } +RUNTIME_FUNCTION(Runtime_FunctionRemovePrototype) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); - return factory->NewJSArrayWithElements(elms); -} + CONVERT_ARG_CHECKED(JSFunction, f, 0); + RUNTIME_ASSERT(f->RemovePrototype()); - -// Returns an array with the property description: -// if args[1] is not a property on args[0] -// returns undefined -// if args[1] is a data property on args[0] -// [false, value, Writeable, Enumerable, Configurable] -// if args[1] is an accessor on args[0] -// [true, GetFunction, SetFunction, Enumerable, Configurable] -RUNTIME_FUNCTION(Runtime_GetOwnProperty) { - HandleScope scope(isolate); - DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); - CONVERT_ARG_HANDLE_CHECKED(Name, name, 1); - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, - GetOwnProperty(isolate, obj, name)); - return *result; + return isolate->heap()->undefined_value(); } -RUNTIME_FUNCTION(Runtime_PreventExtensions) { +RUNTIME_FUNCTION(Runtime_FunctionGetScript) { HandleScope scope(isolate); DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); - Handle result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, - JSObject::PreventExtensions(obj)); - return *result; + + CONVERT_ARG_CHECKED(JSFunction, fun, 0); + Handle script = Handle(fun->shared()->script(), isolate); + if (!script->IsScript()) return isolate->heap()->undefined_value(); + + return *Script::GetWrapper(Handle