// Copyright 2012 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/api.h" #include // For memcpy, strlen. #ifdef V8_USE_ADDRESS_SANITIZER #include #endif // V8_USE_ADDRESS_SANITIZER #include // For isnan. #include "include/v8-debug.h" #include "include/v8-profiler.h" #include "include/v8-testing.h" #include "src/assert-scope.h" #include "src/bootstrapper.h" #include "src/code-stubs.h" #include "src/compiler.h" #include "src/conversions-inl.h" #include "src/counters.h" #include "src/cpu-profiler.h" #include "src/debug.h" #include "src/deoptimizer.h" #include "src/execution.h" #include "src/global-handles.h" #include "src/heap-profiler.h" #include "src/heap-snapshot-generator-inl.h" #include "src/icu_util.h" #include "src/json-parser.h" #include "src/messages.h" #ifdef COMPRESS_STARTUP_DATA_BZ2 #include "src/natives.h" #endif #include "src/parser.h" #include "src/platform.h" #include "src/platform/time.h" #include "src/profile-generator-inl.h" #include "src/property-details.h" #include "src/property.h" #include "src/runtime.h" #include "src/runtime-profiler.h" #include "src/scanner-character-streams.h" #include "src/simulator.h" #include "src/snapshot.h" #include "src/unicode-inl.h" #include "src/utils/random-number-generator.h" #include "src/v8threads.h" #include "src/version.h" #include "src/vm-state-inl.h" #define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr)) #define ENTER_V8(isolate) \ ASSERT((isolate)->IsInitialized()); \ i::VMState __state__((isolate)) namespace v8 { #define ON_BAILOUT(isolate, location, code) \ if (IsExecutionTerminatingCheck(isolate)) { \ code; \ UNREACHABLE(); \ } #define EXCEPTION_PREAMBLE(isolate) \ (isolate)->handle_scope_implementer()->IncrementCallDepth(); \ ASSERT(!(isolate)->external_caught_exception()); \ bool has_pending_exception = false #define EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, do_callback) \ do { \ i::HandleScopeImplementer* handle_scope_implementer = \ (isolate)->handle_scope_implementer(); \ handle_scope_implementer->DecrementCallDepth(); \ if (has_pending_exception) { \ bool call_depth_is_zero = handle_scope_implementer->CallDepthIsZero(); \ (isolate)->OptionalRescheduleException(call_depth_is_zero); \ do_callback \ return value; \ } \ do_callback \ } while (false) #define EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, value) \ EXCEPTION_BAILOUT_CHECK_GENERIC( \ isolate, value, isolate->FireCallCompletedCallback();) #define EXCEPTION_BAILOUT_CHECK(isolate, value) \ EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, ;) // --- E x c e p t i o n B e h a v i o r --- void i::FatalProcessOutOfMemory(const char* location) { i::V8::FatalProcessOutOfMemory(location, false); } // When V8 cannot allocated memory FatalProcessOutOfMemory is called. // The default fatal error handler is called and execution is stopped. void i::V8::FatalProcessOutOfMemory(const char* location, bool take_snapshot) { i::HeapStats heap_stats; int start_marker; heap_stats.start_marker = &start_marker; int new_space_size; heap_stats.new_space_size = &new_space_size; int new_space_capacity; heap_stats.new_space_capacity = &new_space_capacity; intptr_t old_pointer_space_size; heap_stats.old_pointer_space_size = &old_pointer_space_size; intptr_t old_pointer_space_capacity; heap_stats.old_pointer_space_capacity = &old_pointer_space_capacity; intptr_t old_data_space_size; heap_stats.old_data_space_size = &old_data_space_size; intptr_t old_data_space_capacity; heap_stats.old_data_space_capacity = &old_data_space_capacity; intptr_t code_space_size; heap_stats.code_space_size = &code_space_size; intptr_t code_space_capacity; heap_stats.code_space_capacity = &code_space_capacity; intptr_t map_space_size; heap_stats.map_space_size = &map_space_size; intptr_t map_space_capacity; heap_stats.map_space_capacity = &map_space_capacity; intptr_t cell_space_size; heap_stats.cell_space_size = &cell_space_size; intptr_t cell_space_capacity; heap_stats.cell_space_capacity = &cell_space_capacity; intptr_t property_cell_space_size; heap_stats.property_cell_space_size = &property_cell_space_size; intptr_t property_cell_space_capacity; heap_stats.property_cell_space_capacity = &property_cell_space_capacity; intptr_t lo_space_size; heap_stats.lo_space_size = &lo_space_size; int global_handle_count; heap_stats.global_handle_count = &global_handle_count; int weak_global_handle_count; heap_stats.weak_global_handle_count = &weak_global_handle_count; int pending_global_handle_count; heap_stats.pending_global_handle_count = &pending_global_handle_count; int near_death_global_handle_count; heap_stats.near_death_global_handle_count = &near_death_global_handle_count; int free_global_handle_count; heap_stats.free_global_handle_count = &free_global_handle_count; intptr_t memory_allocator_size; heap_stats.memory_allocator_size = &memory_allocator_size; intptr_t memory_allocator_capacity; heap_stats.memory_allocator_capacity = &memory_allocator_capacity; int objects_per_type[LAST_TYPE + 1] = {0}; heap_stats.objects_per_type = objects_per_type; int size_per_type[LAST_TYPE + 1] = {0}; heap_stats.size_per_type = size_per_type; int os_error; heap_stats.os_error = &os_error; int end_marker; heap_stats.end_marker = &end_marker; i::Isolate* isolate = i::Isolate::Current(); if (isolate->heap()->HasBeenSetUp()) { // BUG(1718): Don't use the take_snapshot since we don't support // HeapIterator here without doing a special GC. isolate->heap()->RecordStats(&heap_stats, false); } Utils::ApiCheck(false, location, "Allocation failed - process out of memory"); // If the fatal error handler returns, we stop execution. FATAL("API fatal error handler returned after process out of memory"); } void Utils::ReportApiFailure(const char* location, const char* message) { i::Isolate* isolate = i::Isolate::Current(); FatalErrorCallback callback = isolate->exception_behavior(); if (callback == NULL) { i::OS::PrintError("\n#\n# Fatal error in %s\n# %s\n#\n\n", location, message); i::OS::Abort(); } else { callback(location, message); } isolate->SignalFatalError(); } bool V8::IsDead() { i::Isolate* isolate = i::Isolate::Current(); return isolate->IsDead(); } static inline bool IsExecutionTerminatingCheck(i::Isolate* isolate) { if (!isolate->IsInitialized()) return false; if (isolate->has_scheduled_exception()) { return isolate->scheduled_exception() == isolate->heap()->termination_exception(); } return false; } // --- S t a t i c s --- static bool InitializeHelper(i::Isolate* isolate) { // If the isolate has a function entry hook, it needs to re-build all its // code stubs with entry hooks embedded, so let's deserialize a snapshot. if (isolate == NULL || isolate->function_entry_hook() == NULL) { if (i::Snapshot::Initialize()) return true; } return i::V8::Initialize(NULL); } static inline bool EnsureInitializedForIsolate(i::Isolate* isolate, const char* location) { return (isolate != NULL && isolate->IsInitialized()) || Utils::ApiCheck(InitializeHelper(isolate), location, "Error initializing V8"); } StartupDataDecompressor::StartupDataDecompressor() : raw_data(i::NewArray(V8::GetCompressedStartupDataCount())) { for (int i = 0; i < V8::GetCompressedStartupDataCount(); ++i) { raw_data[i] = NULL; } } StartupDataDecompressor::~StartupDataDecompressor() { for (int i = 0; i < V8::GetCompressedStartupDataCount(); ++i) { i::DeleteArray(raw_data[i]); } i::DeleteArray(raw_data); } int StartupDataDecompressor::Decompress() { int compressed_data_count = V8::GetCompressedStartupDataCount(); StartupData* compressed_data = i::NewArray(compressed_data_count); V8::GetCompressedStartupData(compressed_data); for (int i = 0; i < compressed_data_count; ++i) { char* decompressed = raw_data[i] = i::NewArray(compressed_data[i].raw_size); if (compressed_data[i].compressed_size != 0) { int result = DecompressData(decompressed, &compressed_data[i].raw_size, compressed_data[i].data, compressed_data[i].compressed_size); if (result != 0) return result; } else { ASSERT_EQ(0, compressed_data[i].raw_size); } compressed_data[i].data = decompressed; } V8::SetDecompressedStartupData(compressed_data); i::DeleteArray(compressed_data); return 0; } StartupData::CompressionAlgorithm V8::GetCompressedStartupDataAlgorithm() { #ifdef COMPRESS_STARTUP_DATA_BZ2 return StartupData::kBZip2; #else return StartupData::kUncompressed; #endif } enum CompressedStartupDataItems { kSnapshot = 0, kSnapshotContext, kLibraries, kExperimentalLibraries, kCompressedStartupDataCount }; int V8::GetCompressedStartupDataCount() { #ifdef COMPRESS_STARTUP_DATA_BZ2 return kCompressedStartupDataCount; #else return 0; #endif } void V8::GetCompressedStartupData(StartupData* compressed_data) { #ifdef COMPRESS_STARTUP_DATA_BZ2 compressed_data[kSnapshot].data = reinterpret_cast(i::Snapshot::data()); compressed_data[kSnapshot].compressed_size = i::Snapshot::size(); compressed_data[kSnapshot].raw_size = i::Snapshot::raw_size(); compressed_data[kSnapshotContext].data = reinterpret_cast(i::Snapshot::context_data()); compressed_data[kSnapshotContext].compressed_size = i::Snapshot::context_size(); compressed_data[kSnapshotContext].raw_size = i::Snapshot::context_raw_size(); i::Vector libraries_source = i::Natives::GetScriptsSource(); compressed_data[kLibraries].data = reinterpret_cast(libraries_source.start()); compressed_data[kLibraries].compressed_size = libraries_source.length(); compressed_data[kLibraries].raw_size = i::Natives::GetRawScriptsSize(); i::Vector exp_libraries_source = i::ExperimentalNatives::GetScriptsSource(); compressed_data[kExperimentalLibraries].data = reinterpret_cast(exp_libraries_source.start()); compressed_data[kExperimentalLibraries].compressed_size = exp_libraries_source.length(); compressed_data[kExperimentalLibraries].raw_size = i::ExperimentalNatives::GetRawScriptsSize(); #endif } void V8::SetDecompressedStartupData(StartupData* decompressed_data) { #ifdef COMPRESS_STARTUP_DATA_BZ2 ASSERT_EQ(i::Snapshot::raw_size(), decompressed_data[kSnapshot].raw_size); i::Snapshot::set_raw_data( reinterpret_cast(decompressed_data[kSnapshot].data)); ASSERT_EQ(i::Snapshot::context_raw_size(), decompressed_data[kSnapshotContext].raw_size); i::Snapshot::set_context_raw_data( reinterpret_cast( decompressed_data[kSnapshotContext].data)); ASSERT_EQ(i::Natives::GetRawScriptsSize(), decompressed_data[kLibraries].raw_size); i::Vector libraries_source( decompressed_data[kLibraries].data, decompressed_data[kLibraries].raw_size); i::Natives::SetRawScriptsSource(libraries_source); ASSERT_EQ(i::ExperimentalNatives::GetRawScriptsSize(), decompressed_data[kExperimentalLibraries].raw_size); i::Vector exp_libraries_source( decompressed_data[kExperimentalLibraries].data, decompressed_data[kExperimentalLibraries].raw_size); i::ExperimentalNatives::SetRawScriptsSource(exp_libraries_source); #endif } void V8::SetFatalErrorHandler(FatalErrorCallback that) { i::Isolate* isolate = i::Isolate::UncheckedCurrent(); isolate->set_exception_behavior(that); } void V8::SetAllowCodeGenerationFromStringsCallback( AllowCodeGenerationFromStringsCallback callback) { i::Isolate* isolate = i::Isolate::UncheckedCurrent(); isolate->set_allow_code_gen_callback(callback); } void V8::SetFlagsFromString(const char* str, int length) { i::FlagList::SetFlagsFromString(str, length); } void V8::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags) { i::FlagList::SetFlagsFromCommandLine(argc, argv, remove_flags); } RegisteredExtension* RegisteredExtension::first_extension_ = NULL; RegisteredExtension::RegisteredExtension(Extension* extension) : extension_(extension) { } void RegisteredExtension::Register(RegisteredExtension* that) { that->next_ = first_extension_; first_extension_ = that; } void RegisteredExtension::UnregisterAll() { RegisteredExtension* re = first_extension_; while (re != NULL) { RegisteredExtension* next = re->next(); delete re; re = next; } } void RegisterExtension(Extension* that) { RegisteredExtension* extension = new RegisteredExtension(that); RegisteredExtension::Register(extension); } Extension::Extension(const char* name, const char* source, int dep_count, const char** deps, int source_length) : name_(name), source_length_(source_length >= 0 ? source_length : (source ? static_cast(strlen(source)) : 0)), source_(source, source_length_), dep_count_(dep_count), deps_(deps), auto_enable_(false) { CHECK(source != NULL || source_length_ == 0); } ResourceConstraints::ResourceConstraints() : max_semi_space_size_(0), max_old_space_size_(0), max_executable_size_(0), stack_limit_(NULL), max_available_threads_(0), code_range_size_(0) { } void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory, uint64_t virtual_memory_limit, uint32_t number_of_processors) { #if V8_OS_ANDROID // Android has higher physical memory requirements before raising the maximum // heap size limits since it has no swap space. const uint64_t low_limit = 512ul * i::MB; const uint64_t medium_limit = 1ul * i::GB; const uint64_t high_limit = 2ul * i::GB; #else const uint64_t low_limit = 512ul * i::MB; const uint64_t medium_limit = 768ul * i::MB; const uint64_t high_limit = 1ul * i::GB; #endif if (physical_memory <= low_limit) { set_max_semi_space_size(i::Heap::kMaxSemiSpaceSizeLowMemoryDevice); set_max_old_space_size(i::Heap::kMaxOldSpaceSizeLowMemoryDevice); set_max_executable_size(i::Heap::kMaxExecutableSizeLowMemoryDevice); } else if (physical_memory <= medium_limit) { set_max_semi_space_size(i::Heap::kMaxSemiSpaceSizeMediumMemoryDevice); set_max_old_space_size(i::Heap::kMaxOldSpaceSizeMediumMemoryDevice); set_max_executable_size(i::Heap::kMaxExecutableSizeMediumMemoryDevice); } else if (physical_memory <= high_limit) { set_max_semi_space_size(i::Heap::kMaxSemiSpaceSizeHighMemoryDevice); set_max_old_space_size(i::Heap::kMaxOldSpaceSizeHighMemoryDevice); set_max_executable_size(i::Heap::kMaxExecutableSizeHighMemoryDevice); } else { set_max_semi_space_size(i::Heap::kMaxSemiSpaceSizeHugeMemoryDevice); set_max_old_space_size(i::Heap::kMaxOldSpaceSizeHugeMemoryDevice); set_max_executable_size(i::Heap::kMaxExecutableSizeHugeMemoryDevice); } set_max_available_threads(i::Max(i::Min(number_of_processors, 4u), 1u)); if (virtual_memory_limit > 0 && i::kIs64BitArch) { // Reserve no more than 1/8 of the memory for the code range, but at most // 512 MB. set_code_range_size( i::Min(512, static_cast((virtual_memory_limit >> 3) / i::MB))); } } bool SetResourceConstraints(Isolate* v8_isolate, ResourceConstraints* constraints) { i::Isolate* isolate = reinterpret_cast(v8_isolate); int semi_space_size = constraints->max_semi_space_size(); int old_space_size = constraints->max_old_space_size(); int max_executable_size = constraints->max_executable_size(); int code_range_size = constraints->code_range_size(); if (semi_space_size != 0 || old_space_size != 0 || max_executable_size != 0 || code_range_size != 0) { // After initialization it's too late to change Heap constraints. ASSERT(!isolate->IsInitialized()); bool result = isolate->heap()->ConfigureHeap(semi_space_size, old_space_size, max_executable_size, code_range_size); if (!result) return false; } if (constraints->stack_limit() != NULL) { uintptr_t limit = reinterpret_cast(constraints->stack_limit()); isolate->stack_guard()->SetStackLimit(limit); } isolate->set_max_available_threads(constraints->max_available_threads()); return true; } i::Object** V8::GlobalizeReference(i::Isolate* isolate, i::Object** obj) { LOG_API(isolate, "Persistent::New"); i::Handle result = isolate->global_handles()->Create(*obj); #ifdef DEBUG (*obj)->ObjectVerify(); #endif // DEBUG return result.location(); } i::Object** V8::CopyPersistent(i::Object** obj) { i::Handle result = i::GlobalHandles::CopyGlobal(obj); #ifdef DEBUG (*obj)->ObjectVerify(); #endif // DEBUG return result.location(); } void V8::MakeWeak(i::Object** object, void* parameters, WeakCallback weak_callback) { i::GlobalHandles::MakeWeak(object, parameters, weak_callback); } void* V8::ClearWeak(i::Object** obj) { return i::GlobalHandles::ClearWeakness(obj); } void V8::DisposeGlobal(i::Object** obj) { i::GlobalHandles::Destroy(obj); } void V8::Eternalize(Isolate* v8_isolate, Value* value, int* index) { i::Isolate* isolate = reinterpret_cast(v8_isolate); i::Object* object = *Utils::OpenHandle(value); isolate->eternal_handles()->Create(isolate, object, index); } Local V8::GetEternal(Isolate* v8_isolate, int index) { i::Isolate* isolate = reinterpret_cast(v8_isolate); return Utils::ToLocal(isolate->eternal_handles()->Get(index)); } // --- H a n d l e s --- HandleScope::HandleScope(Isolate* isolate) { Initialize(isolate); } void HandleScope::Initialize(Isolate* isolate) { i::Isolate* internal_isolate = reinterpret_cast(isolate); // We do not want to check the correct usage of the Locker class all over the // place, so we do it only here: Without a HandleScope, an embedder can do // almost nothing, so it is enough to check in this central place. Utils::ApiCheck(!v8::Locker::IsActive() || internal_isolate->thread_manager()->IsLockedByCurrentThread(), "HandleScope::HandleScope", "Entering the V8 API without proper locking in place"); i::HandleScopeData* current = internal_isolate->handle_scope_data(); isolate_ = internal_isolate; prev_next_ = current->next; prev_limit_ = current->limit; current->level++; } HandleScope::~HandleScope() { i::HandleScope::CloseScope(isolate_, prev_next_, prev_limit_); } int HandleScope::NumberOfHandles(Isolate* isolate) { return i::HandleScope::NumberOfHandles( reinterpret_cast(isolate)); } i::Object** HandleScope::CreateHandle(i::Isolate* isolate, i::Object* value) { return i::HandleScope::CreateHandle(isolate, value); } i::Object** HandleScope::CreateHandle(i::HeapObject* heap_object, i::Object* value) { ASSERT(heap_object->IsHeapObject()); return i::HandleScope::CreateHandle(heap_object->GetIsolate(), value); } EscapableHandleScope::EscapableHandleScope(Isolate* v8_isolate) { i::Isolate* isolate = reinterpret_cast(v8_isolate); escape_slot_ = CreateHandle(isolate, isolate->heap()->the_hole_value()); Initialize(v8_isolate); } i::Object** EscapableHandleScope::Escape(i::Object** escape_value) { i::Heap* heap = reinterpret_cast(GetIsolate())->heap(); Utils::ApiCheck(*escape_slot_ == heap->the_hole_value(), "EscapeableHandleScope::Escape", "Escape value set twice"); if (escape_value == NULL) { *escape_slot_ = heap->undefined_value(); return NULL; } *escape_slot_ = *escape_value; return escape_slot_; } void Context::Enter() { i::Handle env = Utils::OpenHandle(this); i::Isolate* isolate = env->GetIsolate(); ENTER_V8(isolate); i::HandleScopeImplementer* impl = isolate->handle_scope_implementer(); impl->EnterContext(env); impl->SaveContext(isolate->context()); isolate->set_context(*env); } void Context::Exit() { i::Handle env = Utils::OpenHandle(this); i::Isolate* isolate = env->GetIsolate(); ENTER_V8(isolate); i::HandleScopeImplementer* impl = isolate->handle_scope_implementer(); if (!Utils::ApiCheck(impl->LastEnteredContextWas(env), "v8::Context::Exit()", "Cannot exit non-entered context")) { return; } impl->LeaveContext(); isolate->set_context(impl->RestoreContext()); } static void* DecodeSmiToAligned(i::Object* value, const char* location) { Utils::ApiCheck(value->IsSmi(), location, "Not a Smi"); return reinterpret_cast(value); } static i::Smi* EncodeAlignedAsSmi(void* value, const char* location) { i::Smi* smi = reinterpret_cast(value); Utils::ApiCheck(smi->IsSmi(), location, "Pointer is not aligned"); return smi; } static i::Handle EmbedderDataFor(Context* context, int index, bool can_grow, const char* location) { i::Handle env = Utils::OpenHandle(context); bool ok = Utils::ApiCheck(env->IsNativeContext(), location, "Not a native context") && Utils::ApiCheck(index >= 0, location, "Negative index"); if (!ok) return i::Handle(); i::Handle data(env->embedder_data()); if (index < data->length()) return data; if (!Utils::ApiCheck(can_grow, location, "Index too large")) { return i::Handle(); } int new_size = i::Max(index, data->length() << 1) + 1; data = i::FixedArray::CopySize(data, new_size); env->set_embedder_data(*data); return data; } v8::Local Context::SlowGetEmbedderData(int index) { const char* location = "v8::Context::GetEmbedderData()"; i::Handle data = EmbedderDataFor(this, index, false, location); if (data.is_null()) return Local(); i::Handle result(data->get(index), data->GetIsolate()); return Utils::ToLocal(result); } void Context::SetEmbedderData(int index, v8::Handle value) { const char* location = "v8::Context::SetEmbedderData()"; i::Handle data = EmbedderDataFor(this, index, true, location); if (data.is_null()) return; i::Handle val = Utils::OpenHandle(*value); data->set(index, *val); ASSERT_EQ(*Utils::OpenHandle(*value), *Utils::OpenHandle(*GetEmbedderData(index))); } void* Context::SlowGetAlignedPointerFromEmbedderData(int index) { const char* location = "v8::Context::GetAlignedPointerFromEmbedderData()"; i::Handle data = EmbedderDataFor(this, index, false, location); if (data.is_null()) return NULL; return DecodeSmiToAligned(data->get(index), location); } void Context::SetAlignedPointerInEmbedderData(int index, void* value) { const char* location = "v8::Context::SetAlignedPointerInEmbedderData()"; i::Handle data = EmbedderDataFor(this, index, true, location); data->set(index, EncodeAlignedAsSmi(value, location)); ASSERT_EQ(value, GetAlignedPointerFromEmbedderData(index)); } // --- N e a n d e r --- // A constructor cannot easily return an error value, therefore it is necessary // to check for a dead VM with ON_BAILOUT before constructing any Neander // objects. To remind you about this there is no HandleScope in the // NeanderObject constructor. When you add one to the site calling the // constructor you should check that you ensured the VM was not dead first. NeanderObject::NeanderObject(v8::internal::Isolate* isolate, int size) { EnsureInitializedForIsolate(isolate, "v8::Nowhere"); ENTER_V8(isolate); value_ = isolate->factory()->NewNeanderObject(); i::Handle elements = isolate->factory()->NewFixedArray(size); value_->set_elements(*elements); } int NeanderObject::size() { return i::FixedArray::cast(value_->elements())->length(); } NeanderArray::NeanderArray(v8::internal::Isolate* isolate) : obj_(isolate, 2) { obj_.set(0, i::Smi::FromInt(0)); } int NeanderArray::length() { return i::Smi::cast(obj_.get(0))->value(); } i::Object* NeanderArray::get(int offset) { ASSERT(0 <= offset); ASSERT(offset < length()); return obj_.get(offset + 1); } // This method cannot easily return an error value, therefore it is necessary // to check for a dead VM with ON_BAILOUT before calling it. To remind you // about this there is no HandleScope in this method. When you add one to the // site calling this method you should check that you ensured the VM was not // dead first. void NeanderArray::add(i::Handle value) { int length = this->length(); int size = obj_.size(); if (length == size - 1) { i::Factory* factory = i::Isolate::Current()->factory(); i::Handle new_elms = factory->NewFixedArray(2 * size); for (int i = 0; i < length; i++) new_elms->set(i + 1, get(i)); obj_.value()->set_elements(*new_elms); } obj_.set(length + 1, *value); obj_.set(0, i::Smi::FromInt(length + 1)); } void NeanderArray::set(int index, i::Object* value) { if (index < 0 || index >= this->length()) return; obj_.set(index + 1, value); } // --- T e m p l a t e --- static void InitializeTemplate(i::Handle that, int type) { that->set_tag(i::Smi::FromInt(type)); } static void TemplateSet(i::Isolate* isolate, v8::Template* templ, int length, v8::Handle* data) { i::Handle list(Utils::OpenHandle(templ)->property_list(), isolate); if (list->IsUndefined()) { list = NeanderArray(isolate).value(); Utils::OpenHandle(templ)->set_property_list(*list); } NeanderArray array(list); array.add(isolate->factory()->NewNumberFromInt(length)); for (int i = 0; i < length; i++) { i::Handle value = data[i].IsEmpty() ? i::Handle(isolate->factory()->undefined_value()) : Utils::OpenHandle(*data[i]); array.add(value); } } void Template::Set(v8::Handle name, v8::Handle value, v8::PropertyAttribute attribute) { i::Isolate* isolate = i::Isolate::Current(); ENTER_V8(isolate); i::HandleScope scope(isolate); const int kSize = 3; v8::Isolate* v8_isolate = reinterpret_cast(isolate); v8::Handle data[kSize] = { name, value, v8::Integer::New(v8_isolate, attribute)}; TemplateSet(isolate, this, kSize, data); } void Template::SetAccessorProperty( v8::Local name, v8::Local getter, v8::Local setter, v8::PropertyAttribute attribute, v8::AccessControl access_control) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); ASSERT(!name.IsEmpty()); ASSERT(!getter.IsEmpty() || !setter.IsEmpty()); i::HandleScope scope(isolate); const int kSize = 5; v8::Isolate* v8_isolate = reinterpret_cast(isolate); v8::Handle data[kSize] = { name, getter, setter, v8::Integer::New(v8_isolate, attribute), v8::Integer::New(v8_isolate, access_control)}; TemplateSet(isolate, this, kSize, data); } // --- F u n c t i o n T e m p l a t e --- static void InitializeFunctionTemplate( i::Handle info) { info->set_tag(i::Smi::FromInt(Consts::FUNCTION_TEMPLATE)); info->set_flag(0); } Local FunctionTemplate::PrototypeTemplate() { i::Isolate* i_isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(i_isolate); i::Handle result(Utils::OpenHandle(this)->prototype_template(), i_isolate); if (result->IsUndefined()) { v8::Isolate* isolate = reinterpret_cast(i_isolate); result = Utils::OpenHandle(*ObjectTemplate::New(isolate)); Utils::OpenHandle(this)->set_prototype_template(*result); } return ToApiHandle(result); } void FunctionTemplate::Inherit(v8::Handle value) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); Utils::OpenHandle(this)->set_parent_template(*Utils::OpenHandle(*value)); } static Local FunctionTemplateNew( i::Isolate* isolate, FunctionCallback callback, v8::Handle data, v8::Handle signature, int length, bool do_not_cache) { i::Handle struct_obj = isolate->factory()->NewStruct(i::FUNCTION_TEMPLATE_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); InitializeFunctionTemplate(obj); obj->set_do_not_cache(do_not_cache); int next_serial_number = 0; if (!do_not_cache) { next_serial_number = isolate->next_serial_number() + 1; isolate->set_next_serial_number(next_serial_number); } obj->set_serial_number(i::Smi::FromInt(next_serial_number)); if (callback != 0) { if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } Utils::ToLocal(obj)->SetCallHandler(callback, data); } obj->set_length(length); obj->set_undetectable(false); obj->set_needs_access_check(false); if (!signature.IsEmpty()) obj->set_signature(*Utils::OpenHandle(*signature)); return Utils::ToLocal(obj); } Local FunctionTemplate::New( Isolate* isolate, FunctionCallback callback, v8::Handle data, v8::Handle signature, int length) { i::Isolate* i_isolate = reinterpret_cast(isolate); EnsureInitializedForIsolate(i_isolate, "v8::FunctionTemplate::New()"); LOG_API(i_isolate, "FunctionTemplate::New"); ENTER_V8(i_isolate); return FunctionTemplateNew( i_isolate, callback, data, signature, length, false); } Local Signature::New(Isolate* isolate, Handle receiver, int argc, Handle argv[]) { i::Isolate* i_isolate = reinterpret_cast(isolate); EnsureInitializedForIsolate(i_isolate, "v8::Signature::New()"); LOG_API(i_isolate, "Signature::New"); ENTER_V8(i_isolate); i::Handle struct_obj = i_isolate->factory()->NewStruct(i::SIGNATURE_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); if (!receiver.IsEmpty()) obj->set_receiver(*Utils::OpenHandle(*receiver)); if (argc > 0) { i::Handle args = i_isolate->factory()->NewFixedArray(argc); for (int i = 0; i < argc; i++) { if (!argv[i].IsEmpty()) args->set(i, *Utils::OpenHandle(*argv[i])); } obj->set_args(*args); } return Utils::ToLocal(obj); } Local AccessorSignature::New( Isolate* isolate, Handle receiver) { return Utils::AccessorSignatureToLocal(Utils::OpenHandle(*receiver)); } template static Local NewDescriptor( Isolate* isolate, const i::DeclaredAccessorDescriptorData& data, Data* previous_descriptor) { i::Isolate* internal_isolate = reinterpret_cast(isolate); i::Handle previous = i::Handle(); if (previous_descriptor != NULL) { previous = Utils::OpenHandle( static_cast(previous_descriptor)); } i::Handle descriptor = i::DeclaredAccessorDescriptor::Create(internal_isolate, data, previous); return Utils::Convert(descriptor); } Local ObjectOperationDescriptor::NewInternalFieldDereference( Isolate* isolate, int internal_field) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorObjectDereference; data.object_dereference_descriptor.internal_field = internal_field; return NewDescriptor(isolate, data, NULL); } Local RawOperationDescriptor::NewRawShift( Isolate* isolate, int16_t byte_offset) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorPointerShift; data.pointer_shift_descriptor.byte_offset = byte_offset; return NewDescriptor(isolate, data, this); } Local RawOperationDescriptor::NewHandleDereference( Isolate* isolate) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorReturnObject; return NewDescriptor(isolate, data, this); } Local RawOperationDescriptor::NewRawDereference( Isolate* isolate) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorPointerDereference; return NewDescriptor(isolate, data, this); } Local RawOperationDescriptor::NewPointerCompare( Isolate* isolate, void* compare_value) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorPointerCompare; data.pointer_compare_descriptor.compare_value = compare_value; return NewDescriptor(isolate, data, this); } Local RawOperationDescriptor::NewPrimitiveValue( Isolate* isolate, DeclaredAccessorDescriptorDataType data_type, uint8_t bool_offset) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorPrimitiveValue; data.primitive_value_descriptor.data_type = data_type; data.primitive_value_descriptor.bool_offset = bool_offset; return NewDescriptor(isolate, data, this); } template static Local NewBitmaskCompare( Isolate* isolate, T bitmask, T compare_value, RawOperationDescriptor* operation) { i::DeclaredAccessorDescriptorData data; data.type = i::kDescriptorBitmaskCompare; data.bitmask_compare_descriptor.bitmask = bitmask; data.bitmask_compare_descriptor.compare_value = compare_value; data.bitmask_compare_descriptor.size = sizeof(T); return NewDescriptor(isolate, data, operation); } Local RawOperationDescriptor::NewBitmaskCompare8( Isolate* isolate, uint8_t bitmask, uint8_t compare_value) { return NewBitmaskCompare(isolate, bitmask, compare_value, this); } Local RawOperationDescriptor::NewBitmaskCompare16( Isolate* isolate, uint16_t bitmask, uint16_t compare_value) { return NewBitmaskCompare(isolate, bitmask, compare_value, this); } Local RawOperationDescriptor::NewBitmaskCompare32( Isolate* isolate, uint32_t bitmask, uint32_t compare_value) { return NewBitmaskCompare(isolate, bitmask, compare_value, this); } Local TypeSwitch::New(Handle type) { Handle types[1] = { type }; return TypeSwitch::New(1, types); } Local TypeSwitch::New(int argc, Handle types[]) { i::Isolate* isolate = i::Isolate::Current(); EnsureInitializedForIsolate(isolate, "v8::TypeSwitch::New()"); LOG_API(isolate, "TypeSwitch::New"); ENTER_V8(isolate); i::Handle vector = isolate->factory()->NewFixedArray(argc); for (int i = 0; i < argc; i++) vector->set(i, *Utils::OpenHandle(*types[i])); i::Handle struct_obj = isolate->factory()->NewStruct(i::TYPE_SWITCH_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); obj->set_types(*vector); return Utils::ToLocal(obj); } int TypeSwitch::match(v8::Handle value) { i::Isolate* isolate = i::Isolate::Current(); LOG_API(isolate, "TypeSwitch::match"); USE(isolate); i::Handle obj = Utils::OpenHandle(*value); i::Handle info = Utils::OpenHandle(this); i::FixedArray* types = i::FixedArray::cast(info->types()); for (int i = 0; i < types->length(); i++) { if (i::FunctionTemplateInfo::cast(types->get(i))->IsTemplateFor(*obj)) return i + 1; } return 0; } #define SET_FIELD_WRAPPED(obj, setter, cdata) do { \ i::Handle foreign = FromCData(obj->GetIsolate(), cdata); \ (obj)->setter(*foreign); \ } while (false) void FunctionTemplate::SetCallHandler(FunctionCallback callback, v8::Handle data) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle struct_obj = isolate->factory()->NewStruct(i::CALL_HANDLER_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); SET_FIELD_WRAPPED(obj, set_callback, callback); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } obj->set_data(*Utils::OpenHandle(*data)); Utils::OpenHandle(this)->set_call_code(*obj); } static i::Handle SetAccessorInfoProperties( i::Handle obj, v8::Handle name, v8::AccessControl settings, v8::PropertyAttribute attributes, v8::Handle signature) { obj->set_name(*Utils::OpenHandle(*name)); if (settings & ALL_CAN_READ) obj->set_all_can_read(true); if (settings & ALL_CAN_WRITE) obj->set_all_can_write(true); obj->set_property_attributes(static_cast(attributes)); if (!signature.IsEmpty()) { obj->set_expected_receiver_type(*Utils::OpenHandle(*signature)); } return obj; } template static i::Handle MakeAccessorInfo( v8::Handle name, Getter getter, Setter setter, v8::Handle data, v8::AccessControl settings, v8::PropertyAttribute attributes, v8::Handle signature) { i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate(); i::Handle obj = isolate->factory()->NewExecutableAccessorInfo(); SET_FIELD_WRAPPED(obj, set_getter, getter); SET_FIELD_WRAPPED(obj, set_setter, setter); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } obj->set_data(*Utils::OpenHandle(*data)); return SetAccessorInfoProperties(obj, name, settings, attributes, signature); } static i::Handle MakeAccessorInfo( v8::Handle name, v8::Handle descriptor, void* setter_ignored, void* data_ignored, v8::AccessControl settings, v8::PropertyAttribute attributes, v8::Handle signature) { i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate(); if (descriptor.IsEmpty()) return i::Handle(); i::Handle obj = isolate->factory()->NewDeclaredAccessorInfo(); obj->set_descriptor(*Utils::OpenHandle(*descriptor)); return SetAccessorInfoProperties(obj, name, settings, attributes, signature); } Local FunctionTemplate::InstanceTemplate() { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); if (!Utils::ApiCheck(this != NULL, "v8::FunctionTemplate::InstanceTemplate()", "Reading from empty handle")) { return Local(); } ENTER_V8(isolate); i::Handle handle = Utils::OpenHandle(this); if (handle->instance_template()->IsUndefined()) { Local templ = ObjectTemplate::New(isolate, ToApiHandle(handle)); handle->set_instance_template(*Utils::OpenHandle(*templ)); } i::Handle result( i::ObjectTemplateInfo::cast(handle->instance_template())); return Utils::ToLocal(result); } void FunctionTemplate::SetLength(int length) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); Utils::OpenHandle(this)->set_length(length); } void FunctionTemplate::SetClassName(Handle name) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); Utils::OpenHandle(this)->set_class_name(*Utils::OpenHandle(*name)); } void FunctionTemplate::SetHiddenPrototype(bool value) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); Utils::OpenHandle(this)->set_hidden_prototype(value); } void FunctionTemplate::ReadOnlyPrototype() { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); Utils::OpenHandle(this)->set_read_only_prototype(true); } void FunctionTemplate::RemovePrototype() { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); Utils::OpenHandle(this)->set_remove_prototype(true); } // --- O b j e c t T e m p l a t e --- Local ObjectTemplate::New(Isolate* isolate) { return New(reinterpret_cast(isolate), Local()); } Local ObjectTemplate::New() { return New(i::Isolate::Current(), Local()); } Local ObjectTemplate::New( i::Isolate* isolate, v8::Handle constructor) { EnsureInitializedForIsolate(isolate, "v8::ObjectTemplate::New()"); LOG_API(isolate, "ObjectTemplate::New"); ENTER_V8(isolate); i::Handle struct_obj = isolate->factory()->NewStruct(i::OBJECT_TEMPLATE_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); InitializeTemplate(obj, Consts::OBJECT_TEMPLATE); if (!constructor.IsEmpty()) obj->set_constructor(*Utils::OpenHandle(*constructor)); obj->set_internal_field_count(i::Smi::FromInt(0)); return Utils::ToLocal(obj); } // Ensure that the object template has a constructor. If no // constructor is available we create one. static i::Handle EnsureConstructor( i::Isolate* isolate, ObjectTemplate* object_template) { i::Object* obj = Utils::OpenHandle(object_template)->constructor(); if (!obj ->IsUndefined()) { i::FunctionTemplateInfo* info = i::FunctionTemplateInfo::cast(obj); return i::Handle(info, isolate); } Local templ = FunctionTemplate::New(reinterpret_cast(isolate)); i::Handle constructor = Utils::OpenHandle(*templ); constructor->set_instance_template(*Utils::OpenHandle(object_template)); Utils::OpenHandle(object_template)->set_constructor(*constructor); return constructor; } static inline void AddPropertyToTemplate( i::Handle info, i::Handle obj) { i::Isolate* isolate = info->GetIsolate(); i::Handle list(info->property_accessors(), isolate); if (list->IsUndefined()) { list = NeanderArray(isolate).value(); info->set_property_accessors(*list); } NeanderArray array(list); array.add(obj); } static inline i::Handle GetTemplateInfo( i::Isolate* isolate, Template* template_obj) { return Utils::OpenHandle(template_obj); } // TODO(dcarney): remove this with ObjectTemplate::SetAccessor static inline i::Handle GetTemplateInfo( i::Isolate* isolate, ObjectTemplate* object_template) { EnsureConstructor(isolate, object_template); return Utils::OpenHandle(object_template); } template static bool TemplateSetAccessor( Template* template_obj, v8::Local name, Getter getter, Setter setter, Data data, AccessControl settings, PropertyAttribute attribute, v8::Local signature) { i::Isolate* isolate = Utils::OpenHandle(template_obj)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle obj = MakeAccessorInfo( name, getter, setter, data, settings, attribute, signature); if (obj.is_null()) return false; i::Handle info = GetTemplateInfo(isolate, template_obj); AddPropertyToTemplate(info, obj); return true; } bool Template::SetDeclaredAccessor( Local name, Local descriptor, PropertyAttribute attribute, Local signature, AccessControl settings) { void* null = NULL; return TemplateSetAccessor( this, name, descriptor, null, null, settings, attribute, signature); } void Template::SetNativeDataProperty(v8::Local name, AccessorGetterCallback getter, AccessorSetterCallback setter, v8::Handle data, PropertyAttribute attribute, v8::Local signature, AccessControl settings) { TemplateSetAccessor( this, name, getter, setter, data, settings, attribute, signature); } void ObjectTemplate::SetAccessor(v8::Handle name, AccessorGetterCallback getter, AccessorSetterCallback setter, v8::Handle data, AccessControl settings, PropertyAttribute attribute, v8::Handle signature) { TemplateSetAccessor( this, name, getter, setter, data, settings, attribute, signature); } void ObjectTemplate::SetNamedPropertyHandler( NamedPropertyGetterCallback getter, NamedPropertySetterCallback setter, NamedPropertyQueryCallback query, NamedPropertyDeleterCallback remover, NamedPropertyEnumeratorCallback enumerator, Handle data) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); EnsureConstructor(isolate, this); i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast( Utils::OpenHandle(this)->constructor()); i::Handle cons(constructor); i::Handle struct_obj = isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); if (getter != 0) SET_FIELD_WRAPPED(obj, set_getter, getter); if (setter != 0) SET_FIELD_WRAPPED(obj, set_setter, setter); if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query); if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover); if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } obj->set_data(*Utils::OpenHandle(*data)); cons->set_named_property_handler(*obj); } void ObjectTemplate::MarkAsUndetectable() { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); EnsureConstructor(isolate, this); i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor()); i::Handle cons(constructor); cons->set_undetectable(true); } void ObjectTemplate::SetAccessCheckCallbacks( NamedSecurityCallback named_callback, IndexedSecurityCallback indexed_callback, Handle data, bool turned_on_by_default) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); EnsureConstructor(isolate, this); i::Handle struct_info = isolate->factory()->NewStruct(i::ACCESS_CHECK_INFO_TYPE); i::Handle info = i::Handle::cast(struct_info); SET_FIELD_WRAPPED(info, set_named_callback, named_callback); SET_FIELD_WRAPPED(info, set_indexed_callback, indexed_callback); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } info->set_data(*Utils::OpenHandle(*data)); i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor()); i::Handle cons(constructor); cons->set_access_check_info(*info); cons->set_needs_access_check(turned_on_by_default); } void ObjectTemplate::SetIndexedPropertyHandler( IndexedPropertyGetterCallback getter, IndexedPropertySetterCallback setter, IndexedPropertyQueryCallback query, IndexedPropertyDeleterCallback remover, IndexedPropertyEnumeratorCallback enumerator, Handle data) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); EnsureConstructor(isolate, this); i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast( Utils::OpenHandle(this)->constructor()); i::Handle cons(constructor); i::Handle struct_obj = isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); if (getter != 0) SET_FIELD_WRAPPED(obj, set_getter, getter); if (setter != 0) SET_FIELD_WRAPPED(obj, set_setter, setter); if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query); if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover); if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } obj->set_data(*Utils::OpenHandle(*data)); cons->set_indexed_property_handler(*obj); } void ObjectTemplate::SetCallAsFunctionHandler(FunctionCallback callback, Handle data) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ENTER_V8(isolate); i::HandleScope scope(isolate); EnsureConstructor(isolate, this); i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast( Utils::OpenHandle(this)->constructor()); i::Handle cons(constructor); i::Handle struct_obj = isolate->factory()->NewStruct(i::CALL_HANDLER_INFO_TYPE); i::Handle obj = i::Handle::cast(struct_obj); SET_FIELD_WRAPPED(obj, set_callback, callback); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(isolate)); } obj->set_data(*Utils::OpenHandle(*data)); cons->set_instance_call_handler(*obj); } int ObjectTemplate::InternalFieldCount() { return i::Smi::cast(Utils::OpenHandle(this)->internal_field_count())->value(); } void ObjectTemplate::SetInternalFieldCount(int value) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); if (!Utils::ApiCheck(i::Smi::IsValid(value), "v8::ObjectTemplate::SetInternalFieldCount()", "Invalid internal field count")) { return; } ENTER_V8(isolate); if (value > 0) { // The internal field count is set by the constructor function's // construct code, so we ensure that there is a constructor // function to do the setting. EnsureConstructor(isolate, this); } Utils::OpenHandle(this)->set_internal_field_count(i::Smi::FromInt(value)); } // --- S c r i p t s --- // Internally, UnboundScript is a SharedFunctionInfo, and Script is a // JSFunction. ScriptCompiler::CachedData::CachedData(const uint8_t* data_, int length_, BufferPolicy buffer_policy_) : data(data_), length(length_), buffer_policy(buffer_policy_) {} ScriptCompiler::CachedData::~CachedData() { if (buffer_policy == BufferOwned) { delete[] data; } } Local