summaryrefslogtreecommitdiff
path: root/src/3rdparty/v8/src/builtins.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/v8/src/builtins.cc')
-rw-r--r--src/3rdparty/v8/src/builtins.cc1708
1 files changed, 1708 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/builtins.cc b/src/3rdparty/v8/src/builtins.cc
new file mode 100644
index 0000000..ae3dab4
--- /dev/null
+++ b/src/3rdparty/v8/src/builtins.cc
@@ -0,0 +1,1708 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "api.h"
+#include "arguments.h"
+#include "bootstrapper.h"
+#include "builtins.h"
+#include "gdb-jit.h"
+#include "ic-inl.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+// Arguments object passed to C++ builtins.
+template <BuiltinExtraArguments extra_args>
+class BuiltinArguments : public Arguments {
+ public:
+ BuiltinArguments(int length, Object** arguments)
+ : Arguments(length, arguments) { }
+
+ Object*& operator[] (int index) {
+ ASSERT(index < length());
+ return Arguments::operator[](index);
+ }
+
+ template <class S> Handle<S> at(int index) {
+ ASSERT(index < length());
+ return Arguments::at<S>(index);
+ }
+
+ Handle<Object> receiver() {
+ return Arguments::at<Object>(0);
+ }
+
+ Handle<JSFunction> called_function() {
+ STATIC_ASSERT(extra_args == NEEDS_CALLED_FUNCTION);
+ return Arguments::at<JSFunction>(Arguments::length() - 1);
+ }
+
+ // Gets the total number of arguments including the receiver (but
+ // excluding extra arguments).
+ int length() const {
+ STATIC_ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ return Arguments::length();
+ }
+
+#ifdef DEBUG
+ void Verify() {
+ // Check we have at least the receiver.
+ ASSERT(Arguments::length() >= 1);
+ }
+#endif
+};
+
+
+// Specialize BuiltinArguments for the called function extra argument.
+
+template <>
+int BuiltinArguments<NEEDS_CALLED_FUNCTION>::length() const {
+ return Arguments::length() - 1;
+}
+
+#ifdef DEBUG
+template <>
+void BuiltinArguments<NEEDS_CALLED_FUNCTION>::Verify() {
+ // Check we have at least the receiver and the called function.
+ ASSERT(Arguments::length() >= 2);
+ // Make sure cast to JSFunction succeeds.
+ called_function();
+}
+#endif
+
+
+#define DEF_ARG_TYPE(name, spec) \
+ typedef BuiltinArguments<spec> name##ArgumentsType;
+BUILTIN_LIST_C(DEF_ARG_TYPE)
+#undef DEF_ARG_TYPE
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+// Support macro for defining builtins in C++.
+// ----------------------------------------------------------------------------
+//
+// A builtin function is defined by writing:
+//
+// BUILTIN(name) {
+// ...
+// }
+//
+// In the body of the builtin function the arguments can be accessed
+// through the BuiltinArguments object args.
+
+#ifdef DEBUG
+
+#define BUILTIN(name) \
+ MUST_USE_RESULT static MaybeObject* Builtin_Impl_##name( \
+ name##ArgumentsType args, Isolate* isolate); \
+ MUST_USE_RESULT static MaybeObject* Builtin_##name( \
+ name##ArgumentsType args, Isolate* isolate) { \
+ ASSERT(isolate == Isolate::Current()); \
+ args.Verify(); \
+ return Builtin_Impl_##name(args, isolate); \
+ } \
+ MUST_USE_RESULT static MaybeObject* Builtin_Impl_##name( \
+ name##ArgumentsType args, Isolate* isolate)
+
+#else // For release mode.
+
+#define BUILTIN(name) \
+ static MaybeObject* Builtin_##name(name##ArgumentsType args, Isolate* isolate)
+
+#endif
+
+
+static inline bool CalledAsConstructor(Isolate* isolate) {
+#ifdef DEBUG
+ // Calculate the result using a full stack frame iterator and check
+ // that the state of the stack is as we assume it to be in the
+ // code below.
+ StackFrameIterator it;
+ ASSERT(it.frame()->is_exit());
+ it.Advance();
+ StackFrame* frame = it.frame();
+ bool reference_result = frame->is_construct();
+#endif
+ Address fp = Isolate::c_entry_fp(isolate->thread_local_top());
+ // Because we know fp points to an exit frame we can use the relevant
+ // part of ExitFrame::ComputeCallerState directly.
+ const int kCallerOffset = ExitFrameConstants::kCallerFPOffset;
+ Address caller_fp = Memory::Address_at(fp + kCallerOffset);
+ // This inlines the part of StackFrame::ComputeType that grabs the
+ // type of the current frame. Note that StackFrame::ComputeType
+ // has been specialized for each architecture so if any one of them
+ // changes this code has to be changed as well.
+ const int kMarkerOffset = StandardFrameConstants::kMarkerOffset;
+ const Smi* kConstructMarker = Smi::FromInt(StackFrame::CONSTRUCT);
+ Object* marker = Memory::Object_at(caller_fp + kMarkerOffset);
+ bool result = (marker == kConstructMarker);
+ ASSERT_EQ(result, reference_result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+
+BUILTIN(Illegal) {
+ UNREACHABLE();
+ return isolate->heap()->undefined_value(); // Make compiler happy.
+}
+
+
+BUILTIN(EmptyFunction) {
+ return isolate->heap()->undefined_value();
+}
+
+
+BUILTIN(ArrayCodeGeneric) {
+ Heap* heap = isolate->heap();
+ isolate->counters()->array_function_runtime()->Increment();
+
+ JSArray* array;
+ if (CalledAsConstructor(isolate)) {
+ array = JSArray::cast(*args.receiver());
+ } else {
+ // Allocate the JS Array
+ JSFunction* constructor =
+ isolate->context()->global_context()->array_function();
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateJSObject(constructor);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ array = JSArray::cast(obj);
+ }
+
+ // 'array' now contains the JSArray we should initialize.
+ ASSERT(array->HasFastElements());
+
+ // Optimize the case where there is one argument and the argument is a
+ // small smi.
+ if (args.length() == 2) {
+ Object* obj = args[1];
+ if (obj->IsSmi()) {
+ int len = Smi::cast(obj)->value();
+ if (len >= 0 && len < JSObject::kInitialMaxFastElementArray) {
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ array->SetContent(FixedArray::cast(obj));
+ return array;
+ }
+ }
+ // Take the argument as the length.
+ { MaybeObject* maybe_obj = array->Initialize(0);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ return array->SetElementsLength(args[1]);
+ }
+
+ // Optimize the case where there are no parameters passed.
+ if (args.length() == 1) {
+ return array->Initialize(JSArray::kPreallocatedArrayElements);
+ }
+
+ // Take the arguments as elements.
+ int number_of_elements = args.length() - 1;
+ Smi* len = Smi::FromInt(number_of_elements);
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len->value());
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ AssertNoAllocation no_gc;
+ FixedArray* elms = FixedArray::cast(obj);
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ // Fill in the content
+ for (int index = 0; index < number_of_elements; index++) {
+ elms->set(index, args[index+1], mode);
+ }
+
+ // Set length and elements on the array.
+ array->set_elements(FixedArray::cast(obj));
+ array->set_length(len);
+
+ return array;
+}
+
+
+MUST_USE_RESULT static MaybeObject* AllocateJSArray(Heap* heap) {
+ JSFunction* array_function =
+ heap->isolate()->context()->global_context()->array_function();
+ Object* result;
+ { MaybeObject* maybe_result = heap->AllocateJSObject(array_function);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ return result;
+}
+
+
+MUST_USE_RESULT static MaybeObject* AllocateEmptyJSArray(Heap* heap) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateJSArray(heap);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ JSArray* result_array = JSArray::cast(result);
+ result_array->set_length(Smi::FromInt(0));
+ result_array->set_elements(heap->empty_fixed_array());
+ return result_array;
+}
+
+
+static void CopyElements(Heap* heap,
+ AssertNoAllocation* no_gc,
+ FixedArray* dst,
+ int dst_index,
+ FixedArray* src,
+ int src_index,
+ int len) {
+ ASSERT(dst != src); // Use MoveElements instead.
+ ASSERT(dst->map() != HEAP->fixed_cow_array_map());
+ ASSERT(len > 0);
+ CopyWords(dst->data_start() + dst_index,
+ src->data_start() + src_index,
+ len);
+ WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
+ if (mode == UPDATE_WRITE_BARRIER) {
+ heap->RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
+ }
+}
+
+
+static void MoveElements(Heap* heap,
+ AssertNoAllocation* no_gc,
+ FixedArray* dst,
+ int dst_index,
+ FixedArray* src,
+ int src_index,
+ int len) {
+ ASSERT(dst->map() != HEAP->fixed_cow_array_map());
+ memmove(dst->data_start() + dst_index,
+ src->data_start() + src_index,
+ len * kPointerSize);
+ WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
+ if (mode == UPDATE_WRITE_BARRIER) {
+ heap->RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
+ }
+}
+
+
+static void FillWithHoles(Heap* heap, FixedArray* dst, int from, int to) {
+ ASSERT(dst->map() != heap->fixed_cow_array_map());
+ MemsetPointer(dst->data_start() + from, heap->the_hole_value(), to - from);
+}
+
+
+static FixedArray* LeftTrimFixedArray(Heap* heap,
+ FixedArray* elms,
+ int to_trim) {
+ ASSERT(elms->map() != HEAP->fixed_cow_array_map());
+ // For now this trick is only applied to fixed arrays in new and paged space.
+ // In large object space the object's start must coincide with chunk
+ // and thus the trick is just not applicable.
+ ASSERT(!HEAP->lo_space()->Contains(elms));
+
+ STATIC_ASSERT(FixedArray::kMapOffset == 0);
+ STATIC_ASSERT(FixedArray::kLengthOffset == kPointerSize);
+ STATIC_ASSERT(FixedArray::kHeaderSize == 2 * kPointerSize);
+
+ Object** former_start = HeapObject::RawField(elms, 0);
+
+ const int len = elms->length();
+
+ if (to_trim > FixedArray::kHeaderSize / kPointerSize &&
+ !heap->new_space()->Contains(elms)) {
+ // If we are doing a big trim in old space then we zap the space that was
+ // formerly part of the array so that the GC (aided by the card-based
+ // remembered set) won't find pointers to new-space there.
+ Object** zap = reinterpret_cast<Object**>(elms->address());
+ zap++; // Header of filler must be at least one word so skip that.
+ for (int i = 1; i < to_trim; i++) {
+ *zap++ = Smi::FromInt(0);
+ }
+ }
+ // Technically in new space this write might be omitted (except for
+ // debug mode which iterates through the heap), but to play safer
+ // we still do it.
+ heap->CreateFillerObjectAt(elms->address(), to_trim * kPointerSize);
+
+ former_start[to_trim] = heap->fixed_array_map();
+ former_start[to_trim + 1] = Smi::FromInt(len - to_trim);
+
+ return FixedArray::cast(HeapObject::FromAddress(
+ elms->address() + to_trim * kPointerSize));
+}
+
+
+static bool ArrayPrototypeHasNoElements(Heap* heap,
+ Context* global_context,
+ JSObject* array_proto) {
+ // This method depends on non writability of Object and Array prototype
+ // fields.
+ if (array_proto->elements() != heap->empty_fixed_array()) return false;
+ // Hidden prototype
+ array_proto = JSObject::cast(array_proto->GetPrototype());
+ ASSERT(array_proto->elements() == heap->empty_fixed_array());
+ // Object.prototype
+ Object* proto = array_proto->GetPrototype();
+ if (proto == heap->null_value()) return false;
+ array_proto = JSObject::cast(proto);
+ if (array_proto != global_context->initial_object_prototype()) return false;
+ if (array_proto->elements() != heap->empty_fixed_array()) return false;
+ ASSERT(array_proto->GetPrototype()->IsNull());
+ return true;
+}
+
+
+MUST_USE_RESULT
+static inline MaybeObject* EnsureJSArrayWithWritableFastElements(
+ Heap* heap, Object* receiver) {
+ if (!receiver->IsJSArray()) return NULL;
+ JSArray* array = JSArray::cast(receiver);
+ HeapObject* elms = array->elements();
+ if (elms->map() == heap->fixed_array_map()) return elms;
+ if (elms->map() == heap->fixed_cow_array_map()) {
+ return array->EnsureWritableFastElements();
+ }
+ return NULL;
+}
+
+
+static inline bool IsJSArrayFastElementMovingAllowed(Heap* heap,
+ JSArray* receiver) {
+ Context* global_context = heap->isolate()->context()->global_context();
+ JSObject* array_proto =
+ JSObject::cast(global_context->array_function()->prototype());
+ return receiver->GetPrototype() == array_proto &&
+ ArrayPrototypeHasNoElements(heap, global_context, array_proto);
+}
+
+
+MUST_USE_RESULT static MaybeObject* CallJsBuiltin(
+ Isolate* isolate,
+ const char* name,
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
+ HandleScope handleScope(isolate);
+
+ Handle<Object> js_builtin =
+ GetProperty(Handle<JSObject>(
+ isolate->global_context()->builtins()),
+ name);
+ ASSERT(js_builtin->IsJSFunction());
+ Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin));
+ ScopedVector<Object**> argv(args.length() - 1);
+ int n_args = args.length() - 1;
+ for (int i = 0; i < n_args; i++) {
+ argv[i] = args.at<Object>(i + 1).location();
+ }
+ bool pending_exception = false;
+ Handle<Object> result = Execution::Call(function,
+ args.receiver(),
+ n_args,
+ argv.start(),
+ &pending_exception);
+ if (pending_exception) return Failure::Exception();
+ return *result;
+}
+
+
+BUILTIN(ArrayPush) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ Object* elms_obj;
+ { MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver);
+ if (maybe_elms_obj == NULL) {
+ return CallJsBuiltin(isolate, "ArrayPush", args);
+ }
+ if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
+ }
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ JSArray* array = JSArray::cast(receiver);
+
+ int len = Smi::cast(array->length())->value();
+ int to_add = args.length() - 1;
+ if (to_add == 0) {
+ return Smi::FromInt(len);
+ }
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT(to_add <= (Smi::kMaxValue - len));
+
+ int new_length = len + to_add;
+
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* new_elms = FixedArray::cast(obj);
+
+ AssertNoAllocation no_gc;
+ if (len > 0) {
+ CopyElements(heap, &no_gc, new_elms, 0, elms, 0, len);
+ }
+ FillWithHoles(heap, new_elms, new_length, capacity);
+
+ elms = new_elms;
+ array->set_elements(elms);
+ }
+
+ // Add the provided values.
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int index = 0; index < to_add; index++) {
+ elms->set(index + len, args[index + 1], mode);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+ return Smi::FromInt(new_length);
+}
+
+
+BUILTIN(ArrayPop) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ Object* elms_obj;
+ { MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver);
+ if (maybe_elms_obj == NULL) return CallJsBuiltin(isolate, "ArrayPop", args);
+ if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
+ }
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ JSArray* array = JSArray::cast(receiver);
+
+ int len = Smi::cast(array->length())->value();
+ if (len == 0) return heap->undefined_value();
+
+ // Get top element
+ MaybeObject* top = elms->get(len - 1);
+
+ // Set the length.
+ array->set_length(Smi::FromInt(len - 1));
+
+ if (!top->IsTheHole()) {
+ // Delete the top element.
+ elms->set_the_hole(len - 1);
+ return top;
+ }
+
+ top = array->GetPrototype()->GetElement(len - 1);
+
+ return top;
+}
+
+
+BUILTIN(ArrayShift) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ Object* elms_obj;
+ { MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver);
+ if (maybe_elms_obj == NULL)
+ return CallJsBuiltin(isolate, "ArrayShift", args);
+ if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
+ }
+ if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
+ return CallJsBuiltin(isolate, "ArrayShift", args);
+ }
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+ if (len == 0) return heap->undefined_value();
+
+ // Get first element
+ Object* first = elms->get(0);
+ if (first->IsTheHole()) {
+ first = heap->undefined_value();
+ }
+
+ if (!heap->lo_space()->Contains(elms)) {
+ // As elms still in the same space they used to be,
+ // there is no need to update region dirty mark.
+ array->set_elements(LeftTrimFixedArray(heap, elms, 1), SKIP_WRITE_BARRIER);
+ } else {
+ // Shift the elements.
+ AssertNoAllocation no_gc;
+ MoveElements(heap, &no_gc, elms, 0, elms, 1, len - 1);
+ elms->set(len - 1, heap->the_hole_value());
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(len - 1));
+
+ return first;
+}
+
+
+BUILTIN(ArrayUnshift) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ Object* elms_obj;
+ { MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver);
+ if (maybe_elms_obj == NULL)
+ return CallJsBuiltin(isolate, "ArrayUnshift", args);
+ if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
+ }
+ if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
+ return CallJsBuiltin(isolate, "ArrayUnshift", args);
+ }
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+ int to_add = args.length() - 1;
+ int new_length = len + to_add;
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT(to_add <= (Smi::kMaxValue - len));
+
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* new_elms = FixedArray::cast(obj);
+
+ AssertNoAllocation no_gc;
+ if (len > 0) {
+ CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len);
+ }
+ FillWithHoles(heap, new_elms, new_length, capacity);
+
+ elms = new_elms;
+ array->set_elements(elms);
+ } else {
+ AssertNoAllocation no_gc;
+ MoveElements(heap, &no_gc, elms, to_add, elms, 0, len);
+ }
+
+ // Add the provided values.
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < to_add; i++) {
+ elms->set(i, args[i + 1], mode);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+ return Smi::FromInt(new_length);
+}
+
+
+BUILTIN(ArraySlice) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArray* elms;
+ int len = -1;
+ if (receiver->IsJSArray()) {
+ JSArray* array = JSArray::cast(receiver);
+ if (!array->HasFastElements() ||
+ !IsJSArrayFastElementMovingAllowed(heap, array)) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+
+ elms = FixedArray::cast(array->elements());
+ len = Smi::cast(array->length())->value();
+ } else {
+ // Array.slice(arguments, ...) is quite a common idiom (notably more
+ // than 50% of invocations in Web apps). Treat it in C++ as well.
+ Map* arguments_map =
+ isolate->context()->global_context()->arguments_boilerplate()->map();
+
+ bool is_arguments_object_with_fast_elements =
+ receiver->IsJSObject()
+ && JSObject::cast(receiver)->map() == arguments_map
+ && JSObject::cast(receiver)->HasFastElements();
+ if (!is_arguments_object_with_fast_elements) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ elms = FixedArray::cast(JSObject::cast(receiver)->elements());
+ Object* len_obj = JSObject::cast(receiver)
+ ->InObjectPropertyAt(Heap::kArgumentsLengthIndex);
+ if (!len_obj->IsSmi()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ len = Smi::cast(len_obj)->value();
+ if (len > elms->length()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ for (int i = 0; i < len; i++) {
+ if (elms->get(i) == heap->the_hole_value()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ }
+ }
+ ASSERT(len >= 0);
+ int n_arguments = args.length() - 1;
+
+ // Note carefully choosen defaults---if argument is missing,
+ // it's undefined which gets converted to 0 for relative_start
+ // and to len for relative_end.
+ int relative_start = 0;
+ int relative_end = len;
+ if (n_arguments > 0) {
+ Object* arg1 = args[1];
+ if (arg1->IsSmi()) {
+ relative_start = Smi::cast(arg1)->value();
+ } else if (!arg1->IsUndefined()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ if (n_arguments > 1) {
+ Object* arg2 = args[2];
+ if (arg2->IsSmi()) {
+ relative_end = Smi::cast(arg2)->value();
+ } else if (!arg2->IsUndefined()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ }
+ }
+
+ // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
+ int k = (relative_start < 0) ? Max(len + relative_start, 0)
+ : Min(relative_start, len);
+
+ // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
+ int final = (relative_end < 0) ? Max(len + relative_end, 0)
+ : Min(relative_end, len);
+
+ // Calculate the length of result array.
+ int result_len = final - k;
+ if (result_len <= 0) {
+ return AllocateEmptyJSArray(heap);
+ }
+
+ Object* result;
+ { MaybeObject* maybe_result = AllocateJSArray(heap);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ JSArray* result_array = JSArray::cast(result);
+
+ { MaybeObject* maybe_result =
+ heap->AllocateUninitializedFixedArray(result_len);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ FixedArray* result_elms = FixedArray::cast(result);
+
+ AssertNoAllocation no_gc;
+ CopyElements(heap, &no_gc, result_elms, 0, elms, k, result_len);
+
+ // Set elements.
+ result_array->set_elements(result_elms);
+
+ // Set the length.
+ result_array->set_length(Smi::FromInt(result_len));
+ return result_array;
+}
+
+
+BUILTIN(ArraySplice) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ Object* elms_obj;
+ { MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver);
+ if (maybe_elms_obj == NULL)
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj;
+ }
+ if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+
+ int n_arguments = args.length() - 1;
+
+ int relative_start = 0;
+ if (n_arguments > 0) {
+ Object* arg1 = args[1];
+ if (arg1->IsSmi()) {
+ relative_start = Smi::cast(arg1)->value();
+ } else if (!arg1->IsUndefined()) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ }
+ int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
+ : Min(relative_start, len);
+
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+ // given as a request to delete all the elements from the start.
+ // And it differs from the case of undefined delete count.
+ // This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ int actual_delete_count;
+ if (n_arguments == 1) {
+ ASSERT(len - actual_start >= 0);
+ actual_delete_count = len - actual_start;
+ } else {
+ int value = 0; // ToInteger(undefined) == 0
+ if (n_arguments > 1) {
+ Object* arg2 = args[2];
+ if (arg2->IsSmi()) {
+ value = Smi::cast(arg2)->value();
+ } else {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ }
+ actual_delete_count = Min(Max(value, 0), len - actual_start);
+ }
+
+ JSArray* result_array = NULL;
+ if (actual_delete_count == 0) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateEmptyJSArray(heap);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ result_array = JSArray::cast(result);
+ } else {
+ // Allocate result array.
+ Object* result;
+ { MaybeObject* maybe_result = AllocateJSArray(heap);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ result_array = JSArray::cast(result);
+
+ { MaybeObject* maybe_result =
+ heap->AllocateUninitializedFixedArray(actual_delete_count);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ FixedArray* result_elms = FixedArray::cast(result);
+
+ AssertNoAllocation no_gc;
+ // Fill newly created array.
+ CopyElements(heap,
+ &no_gc,
+ result_elms, 0,
+ elms, actual_start,
+ actual_delete_count);
+
+ // Set elements.
+ result_array->set_elements(result_elms);
+
+ // Set the length.
+ result_array->set_length(Smi::FromInt(actual_delete_count));
+ }
+
+ int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
+
+ int new_length = len - actual_delete_count + item_count;
+
+ if (item_count < actual_delete_count) {
+ // Shrink the array.
+ const bool trim_array = !heap->lo_space()->Contains(elms) &&
+ ((actual_start + item_count) <
+ (len - actual_delete_count - actual_start));
+ if (trim_array) {
+ const int delta = actual_delete_count - item_count;
+
+ if (actual_start > 0) {
+ Object** start = elms->data_start();
+ memmove(start + delta, start, actual_start * kPointerSize);
+ }
+
+ elms = LeftTrimFixedArray(heap, elms, delta);
+ array->set_elements(elms, SKIP_WRITE_BARRIER);
+ } else {
+ AssertNoAllocation no_gc;
+ MoveElements(heap, &no_gc,
+ elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ FillWithHoles(heap, elms, new_length, len);
+ }
+ } else if (item_count > actual_delete_count) {
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT((item_count - actual_delete_count) <= (Smi::kMaxValue - len));
+
+ // Check if array need to grow.
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* new_elms = FixedArray::cast(obj);
+
+ AssertNoAllocation no_gc;
+ // Copy the part before actual_start as is.
+ if (actual_start > 0) {
+ CopyElements(heap, &no_gc, new_elms, 0, elms, 0, actual_start);
+ }
+ const int to_copy = len - actual_delete_count - actual_start;
+ if (to_copy > 0) {
+ CopyElements(heap, &no_gc,
+ new_elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ to_copy);
+ }
+ FillWithHoles(heap, new_elms, new_length, capacity);
+
+ elms = new_elms;
+ array->set_elements(elms);
+ } else {
+ AssertNoAllocation no_gc;
+ MoveElements(heap, &no_gc,
+ elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ }
+ }
+
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int k = actual_start; k < actual_start + item_count; k++) {
+ elms->set(k, args[3 + k - actual_start], mode);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+
+ return result_array;
+}
+
+
+BUILTIN(ArrayConcat) {
+ Heap* heap = isolate->heap();
+ Context* global_context = isolate->context()->global_context();
+ JSObject* array_proto =
+ JSObject::cast(global_context->array_function()->prototype());
+ if (!ArrayPrototypeHasNoElements(heap, global_context, array_proto)) {
+ return CallJsBuiltin(isolate, "ArrayConcat", args);
+ }
+
+ // Iterate through all the arguments performing checks
+ // and calculating total length.
+ int n_arguments = args.length();
+ int result_len = 0;
+ for (int i = 0; i < n_arguments; i++) {
+ Object* arg = args[i];
+ if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastElements()
+ || JSArray::cast(arg)->GetPrototype() != array_proto) {
+ return CallJsBuiltin(isolate, "ArrayConcat", args);
+ }
+
+ int len = Smi::cast(JSArray::cast(arg)->length())->value();
+
+ // We shouldn't overflow when adding another len.
+ const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
+ STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
+ USE(kHalfOfMaxInt);
+ result_len += len;
+ ASSERT(result_len >= 0);
+
+ if (result_len > FixedArray::kMaxLength) {
+ return CallJsBuiltin(isolate, "ArrayConcat", args);
+ }
+ }
+
+ if (result_len == 0) {
+ return AllocateEmptyJSArray(heap);
+ }
+
+ // Allocate result.
+ Object* result;
+ { MaybeObject* maybe_result = AllocateJSArray(heap);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ JSArray* result_array = JSArray::cast(result);
+
+ { MaybeObject* maybe_result =
+ heap->AllocateUninitializedFixedArray(result_len);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ FixedArray* result_elms = FixedArray::cast(result);
+
+ // Copy data.
+ AssertNoAllocation no_gc;
+ int start_pos = 0;
+ for (int i = 0; i < n_arguments; i++) {
+ JSArray* array = JSArray::cast(args[i]);
+ int len = Smi::cast(array->length())->value();
+ if (len > 0) {
+ FixedArray* elms = FixedArray::cast(array->elements());
+ CopyElements(heap, &no_gc, result_elms, start_pos, elms, 0, len);
+ start_pos += len;
+ }
+ }
+ ASSERT(start_pos == result_len);
+
+ // Set the length and elements.
+ result_array->set_length(Smi::FromInt(result_len));
+ result_array->set_elements(result_elms);
+
+ return result_array;
+}
+
+
+// -----------------------------------------------------------------------------
+// Strict mode poison pills
+
+
+BUILTIN(StrictArgumentsCallee) {
+ HandleScope scope;
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_arguments_callee", HandleVector<Object>(NULL, 0)));
+}
+
+
+BUILTIN(StrictArgumentsCaller) {
+ HandleScope scope;
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_arguments_caller", HandleVector<Object>(NULL, 0)));
+}
+
+
+BUILTIN(StrictFunctionCaller) {
+ HandleScope scope;
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_function_caller", HandleVector<Object>(NULL, 0)));
+}
+
+
+BUILTIN(StrictFunctionArguments) {
+ HandleScope scope;
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_function_arguments", HandleVector<Object>(NULL, 0)));
+}
+
+
+// -----------------------------------------------------------------------------
+//
+
+
+// Returns the holder JSObject if the function can legally be called
+// with this receiver. Returns Heap::null_value() if the call is
+// illegal. Any arguments that don't fit the expected type is
+// overwritten with undefined. Arguments that do fit the expected
+// type is overwritten with the object in the prototype chain that
+// actually has that type.
+static inline Object* TypeCheck(Heap* heap,
+ int argc,
+ Object** argv,
+ FunctionTemplateInfo* info) {
+ Object* recv = argv[0];
+ Object* sig_obj = info->signature();
+ if (sig_obj->IsUndefined()) return recv;
+ SignatureInfo* sig = SignatureInfo::cast(sig_obj);
+ // If necessary, check the receiver
+ Object* recv_type = sig->receiver();
+
+ Object* holder = recv;
+ if (!recv_type->IsUndefined()) {
+ for (; holder != heap->null_value(); holder = holder->GetPrototype()) {
+ if (holder->IsInstanceOf(FunctionTemplateInfo::cast(recv_type))) {
+ break;
+ }
+ }
+ if (holder == heap->null_value()) return holder;
+ }
+ Object* args_obj = sig->args();
+ // If there is no argument signature we're done
+ if (args_obj->IsUndefined()) return holder;
+ FixedArray* args = FixedArray::cast(args_obj);
+ int length = args->length();
+ if (argc <= length) length = argc - 1;
+ for (int i = 0; i < length; i++) {
+ Object* argtype = args->get(i);
+ if (argtype->IsUndefined()) continue;
+ Object** arg = &argv[-1 - i];
+ Object* current = *arg;
+ for (; current != heap->null_value(); current = current->GetPrototype()) {
+ if (current->IsInstanceOf(FunctionTemplateInfo::cast(argtype))) {
+ *arg = current;
+ break;
+ }
+ }
+ if (current == heap->null_value()) *arg = heap->undefined_value();
+ }
+ return holder;
+}
+
+
+template <bool is_construct>
+MUST_USE_RESULT static MaybeObject* HandleApiCallHelper(
+ BuiltinArguments<NEEDS_CALLED_FUNCTION> args, Isolate* isolate) {
+ ASSERT(is_construct == CalledAsConstructor(isolate));
+ Heap* heap = isolate->heap();
+
+ HandleScope scope(isolate);
+ Handle<JSFunction> function = args.called_function();
+ ASSERT(function->shared()->IsApiFunction());
+
+ FunctionTemplateInfo* fun_data = function->shared()->get_api_func_data();
+ if (is_construct) {
+ Handle<FunctionTemplateInfo> desc(fun_data, isolate);
+ bool pending_exception = false;
+ isolate->factory()->ConfigureInstance(
+ desc, Handle<JSObject>::cast(args.receiver()), &pending_exception);
+ ASSERT(isolate->has_pending_exception() == pending_exception);
+ if (pending_exception) return Failure::Exception();
+ fun_data = *desc;
+ }
+
+ Object* raw_holder = TypeCheck(heap, args.length(), &args[0], fun_data);
+
+ if (raw_holder->IsNull()) {
+ // This function cannot be called with the given receiver. Abort!
+ Handle<Object> obj =
+ isolate->factory()->NewTypeError(
+ "illegal_invocation", HandleVector(&function, 1));
+ return isolate->Throw(*obj);
+ }
+
+ Object* raw_call_data = fun_data->call_code();
+ if (!raw_call_data->IsUndefined()) {
+ CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
+ Object* callback_obj = call_data->callback();
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+ Object* data_obj = call_data->data();
+ Object* result;
+
+ LOG(isolate, ApiObjectAccess("call", JSObject::cast(*args.receiver())));
+ ASSERT(raw_holder->IsJSObject());
+
+ CustomArguments custom(isolate);
+ v8::ImplementationUtilities::PrepareArgumentsData(custom.end(),
+ data_obj, *function, raw_holder);
+
+ v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
+ custom.end(),
+ &args[0] - 1,
+ args.length() - 1,
+ is_construct);
+
+ v8::Handle<v8::Value> value;
+ {
+ // Leaving JavaScript.
+ VMState state(isolate, EXTERNAL);
+ ExternalCallbackScope call_scope(isolate,
+ v8::ToCData<Address>(callback_obj));
+ value = callback(new_args);
+ }
+ if (value.IsEmpty()) {
+ result = heap->undefined_value();
+ } else {
+ result = *reinterpret_cast<Object**>(*value);
+ }
+
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!is_construct || result->IsJSObject()) return result;
+ }
+
+ return *args.receiver();
+}
+
+
+BUILTIN(HandleApiCall) {
+ return HandleApiCallHelper<false>(args, isolate);
+}
+
+
+BUILTIN(HandleApiCallConstruct) {
+ return HandleApiCallHelper<true>(args, isolate);
+}
+
+
+#ifdef DEBUG
+
+static void VerifyTypeCheck(Handle<JSObject> object,
+ Handle<JSFunction> function) {
+ ASSERT(function->shared()->IsApiFunction());
+ FunctionTemplateInfo* info = function->shared()->get_api_func_data();
+ if (info->signature()->IsUndefined()) return;
+ SignatureInfo* signature = SignatureInfo::cast(info->signature());
+ Object* receiver_type = signature->receiver();
+ if (receiver_type->IsUndefined()) return;
+ FunctionTemplateInfo* type = FunctionTemplateInfo::cast(receiver_type);
+ ASSERT(object->IsInstanceOf(type));
+}
+
+#endif
+
+
+BUILTIN(FastHandleApiCall) {
+ ASSERT(!CalledAsConstructor(isolate));
+ Heap* heap = isolate->heap();
+ const bool is_construct = false;
+
+ // We expect four more arguments: callback, function, call data, and holder.
+ const int args_length = args.length() - 4;
+ ASSERT(args_length >= 0);
+
+ Object* callback_obj = args[args_length];
+
+ v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
+ &args[args_length + 1],
+ &args[0] - 1,
+ args_length - 1,
+ is_construct);
+
+#ifdef DEBUG
+ VerifyTypeCheck(Utils::OpenHandle(*new_args.Holder()),
+ Utils::OpenHandle(*new_args.Callee()));
+#endif
+ HandleScope scope(isolate);
+ Object* result;
+ v8::Handle<v8::Value> value;
+ {
+ // Leaving JavaScript.
+ VMState state(isolate, EXTERNAL);
+ ExternalCallbackScope call_scope(isolate,
+ v8::ToCData<Address>(callback_obj));
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+
+ value = callback(new_args);
+ }
+ if (value.IsEmpty()) {
+ result = heap->undefined_value();
+ } else {
+ result = *reinterpret_cast<Object**>(*value);
+ }
+
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return result;
+}
+
+
+// Helper function to handle calls to non-function objects created through the
+// API. The object can be called as either a constructor (using new) or just as
+// a function (without new).
+MUST_USE_RESULT static MaybeObject* HandleApiCallAsFunctionOrConstructor(
+ Isolate* isolate,
+ bool is_construct_call,
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
+ // Non-functions are never called as constructors. Even if this is an object
+ // called as a constructor the delegate call is not a construct call.
+ ASSERT(!CalledAsConstructor(isolate));
+ Heap* heap = isolate->heap();
+
+ Handle<Object> receiver = args.at<Object>(0);
+
+ // Get the object called.
+ JSObject* obj = JSObject::cast(*args.receiver());
+
+ // Get the invocation callback from the function descriptor that was
+ // used to create the called object.
+ ASSERT(obj->map()->has_instance_call_handler());
+ JSFunction* constructor = JSFunction::cast(obj->map()->constructor());
+ ASSERT(constructor->shared()->IsApiFunction());
+ Object* handler =
+ constructor->shared()->get_api_func_data()->instance_call_handler();
+ ASSERT(!handler->IsUndefined());
+ CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
+ Object* callback_obj = call_data->callback();
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+
+ // Get the data for the call and perform the callback.
+ Object* result;
+ {
+ HandleScope scope(isolate);
+ LOG(isolate, ApiObjectAccess("call non-function", obj));
+
+ CustomArguments custom(isolate);
+ v8::ImplementationUtilities::PrepareArgumentsData(custom.end(),
+ call_data->data(), constructor, obj);
+ v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
+ custom.end(),
+ &args[0] - 1,
+ args.length() - 1,
+ is_construct_call);
+ v8::Handle<v8::Value> value;
+ {
+ // Leaving JavaScript.
+ VMState state(isolate, EXTERNAL);
+ ExternalCallbackScope call_scope(isolate,
+ v8::ToCData<Address>(callback_obj));
+ value = callback(new_args);
+ }
+ if (value.IsEmpty()) {
+ result = heap->undefined_value();
+ } else {
+ result = *reinterpret_cast<Object**>(*value);
+ }
+ }
+ // Check for exceptions and return result.
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return result;
+}
+
+
+// Handle calls to non-function objects created through the API. This delegate
+// function is used when the call is a normal function call.
+BUILTIN(HandleApiCallAsFunction) {
+ return HandleApiCallAsFunctionOrConstructor(isolate, false, args);
+}
+
+
+// Handle calls to non-function objects created through the API. This delegate
+// function is used when the call is a construct call.
+BUILTIN(HandleApiCallAsConstructor) {
+ return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
+}
+
+
+static void Generate_LoadIC_ArrayLength(MacroAssembler* masm) {
+ LoadIC::GenerateArrayLength(masm);
+}
+
+
+static void Generate_LoadIC_StringLength(MacroAssembler* masm) {
+ LoadIC::GenerateStringLength(masm, false);
+}
+
+
+static void Generate_LoadIC_StringWrapperLength(MacroAssembler* masm) {
+ LoadIC::GenerateStringLength(masm, true);
+}
+
+
+static void Generate_LoadIC_FunctionPrototype(MacroAssembler* masm) {
+ LoadIC::GenerateFunctionPrototype(masm);
+}
+
+
+static void Generate_LoadIC_Initialize(MacroAssembler* masm) {
+ LoadIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_LoadIC_PreMonomorphic(MacroAssembler* masm) {
+ LoadIC::GeneratePreMonomorphic(masm);
+}
+
+
+static void Generate_LoadIC_Miss(MacroAssembler* masm) {
+ LoadIC::GenerateMiss(masm);
+}
+
+
+static void Generate_LoadIC_Megamorphic(MacroAssembler* masm) {
+ LoadIC::GenerateMegamorphic(masm);
+}
+
+
+static void Generate_LoadIC_Normal(MacroAssembler* masm) {
+ LoadIC::GenerateNormal(masm);
+}
+
+
+static void Generate_KeyedLoadIC_Initialize(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_KeyedLoadIC_Miss(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateMiss(masm);
+}
+
+
+static void Generate_KeyedLoadIC_Generic(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateGeneric(masm);
+}
+
+
+static void Generate_KeyedLoadIC_String(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateString(masm);
+}
+
+
+static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
+ KeyedLoadIC::GeneratePreMonomorphic(masm);
+}
+
+static void Generate_KeyedLoadIC_IndexedInterceptor(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateIndexedInterceptor(masm);
+}
+
+
+static void Generate_StoreIC_Initialize(MacroAssembler* masm) {
+ StoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_StoreIC_Initialize_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_StoreIC_Miss(MacroAssembler* masm) {
+ StoreIC::GenerateMiss(masm);
+}
+
+
+static void Generate_StoreIC_Normal(MacroAssembler* masm) {
+ StoreIC::GenerateNormal(masm);
+}
+
+
+static void Generate_StoreIC_Normal_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateNormal(masm);
+}
+
+
+static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
+ StoreIC::GenerateMegamorphic(masm, kNonStrictMode);
+}
+
+
+static void Generate_StoreIC_Megamorphic_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateMegamorphic(masm, kStrictMode);
+}
+
+
+static void Generate_StoreIC_ArrayLength(MacroAssembler* masm) {
+ StoreIC::GenerateArrayLength(masm);
+}
+
+
+static void Generate_StoreIC_ArrayLength_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateArrayLength(masm);
+}
+
+
+static void Generate_StoreIC_GlobalProxy(MacroAssembler* masm) {
+ StoreIC::GenerateGlobalProxy(masm, kNonStrictMode);
+}
+
+
+static void Generate_StoreIC_GlobalProxy_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateGlobalProxy(masm, kStrictMode);
+}
+
+
+static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateGeneric(masm, kNonStrictMode);
+}
+
+
+static void Generate_KeyedStoreIC_Generic_Strict(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateGeneric(masm, kStrictMode);
+}
+
+
+static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateMiss(masm);
+}
+
+
+static void Generate_KeyedStoreIC_Initialize(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_KeyedStoreIC_Initialize_Strict(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateInitialize(masm);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateLoadICDebugBreak(masm);
+}
+
+
+static void Generate_StoreIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateStoreICDebugBreak(masm);
+}
+
+
+static void Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateKeyedLoadICDebugBreak(masm);
+}
+
+
+static void Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateKeyedStoreICDebugBreak(masm);
+}
+
+
+static void Generate_ConstructCall_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateConstructCallDebugBreak(masm);
+}
+
+
+static void Generate_Return_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateReturnDebugBreak(masm);
+}
+
+
+static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateStubNoRegistersDebugBreak(masm);
+}
+
+
+static void Generate_Slot_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateSlotDebugBreak(masm);
+}
+
+
+static void Generate_PlainReturn_LiveEdit(MacroAssembler* masm) {
+ Debug::GeneratePlainReturnLiveEdit(masm);
+}
+
+
+static void Generate_FrameDropper_LiveEdit(MacroAssembler* masm) {
+ Debug::GenerateFrameDropperLiveEdit(masm);
+}
+#endif
+
+
+Builtins::Builtins() : initialized_(false) {
+ memset(builtins_, 0, sizeof(builtins_[0]) * builtin_count);
+ memset(names_, 0, sizeof(names_[0]) * builtin_count);
+}
+
+
+Builtins::~Builtins() {
+}
+
+
+#define DEF_ENUM_C(name, ignore) FUNCTION_ADDR(Builtin_##name),
+Address const Builtins::c_functions_[cfunction_count] = {
+ BUILTIN_LIST_C(DEF_ENUM_C)
+};
+#undef DEF_ENUM_C
+
+#define DEF_JS_NAME(name, ignore) #name,
+#define DEF_JS_ARGC(ignore, argc) argc,
+const char* const Builtins::javascript_names_[id_count] = {
+ BUILTINS_LIST_JS(DEF_JS_NAME)
+};
+
+int const Builtins::javascript_argc_[id_count] = {
+ BUILTINS_LIST_JS(DEF_JS_ARGC)
+};
+#undef DEF_JS_NAME
+#undef DEF_JS_ARGC
+
+struct BuiltinDesc {
+ byte* generator;
+ byte* c_code;
+ const char* s_name; // name is only used for generating log information.
+ int name;
+ Code::Flags flags;
+ BuiltinExtraArguments extra_args;
+};
+
+class BuiltinFunctionTable {
+ public:
+ BuiltinFunctionTable() {
+ Builtins::InitBuiltinFunctionTable();
+ }
+
+ static const BuiltinDesc* functions() { return functions_; }
+
+ private:
+ static BuiltinDesc functions_[Builtins::builtin_count + 1];
+
+ friend class Builtins;
+};
+
+BuiltinDesc BuiltinFunctionTable::functions_[Builtins::builtin_count + 1];
+
+static const BuiltinFunctionTable builtin_function_table_init;
+
+// Define array of pointers to generators and C builtin functions.
+// We do this in a sort of roundabout way so that we can do the initialization
+// within the lexical scope of Builtins:: and within a context where
+// Code::Flags names a non-abstract type.
+void Builtins::InitBuiltinFunctionTable() {
+ BuiltinDesc* functions = BuiltinFunctionTable::functions_;
+ functions[builtin_count].generator = NULL;
+ functions[builtin_count].c_code = NULL;
+ functions[builtin_count].s_name = NULL;
+ functions[builtin_count].name = builtin_count;
+ functions[builtin_count].flags = static_cast<Code::Flags>(0);
+ functions[builtin_count].extra_args = NO_EXTRA_ARGUMENTS;
+
+#define DEF_FUNCTION_PTR_C(aname, aextra_args) \
+ functions->generator = FUNCTION_ADDR(Generate_Adaptor); \
+ functions->c_code = FUNCTION_ADDR(Builtin_##aname); \
+ functions->s_name = #aname; \
+ functions->name = c_##aname; \
+ functions->flags = Code::ComputeFlags(Code::BUILTIN); \
+ functions->extra_args = aextra_args; \
+ ++functions;
+
+#define DEF_FUNCTION_PTR_A(aname, kind, state, extra) \
+ functions->generator = FUNCTION_ADDR(Generate_##aname); \
+ functions->c_code = NULL; \
+ functions->s_name = #aname; \
+ functions->name = k##aname; \
+ functions->flags = Code::ComputeFlags(Code::kind, \
+ NOT_IN_LOOP, \
+ state, \
+ extra); \
+ functions->extra_args = NO_EXTRA_ARGUMENTS; \
+ ++functions;
+
+ BUILTIN_LIST_C(DEF_FUNCTION_PTR_C)
+ BUILTIN_LIST_A(DEF_FUNCTION_PTR_A)
+ BUILTIN_LIST_DEBUG_A(DEF_FUNCTION_PTR_A)
+
+#undef DEF_FUNCTION_PTR_C
+#undef DEF_FUNCTION_PTR_A
+}
+
+void Builtins::Setup(bool create_heap_objects) {
+ ASSERT(!initialized_);
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
+
+ // Create a scope for the handles in the builtins.
+ HandleScope scope(isolate);
+
+ const BuiltinDesc* functions = BuiltinFunctionTable::functions();
+
+ // For now we generate builtin adaptor code into a stack-allocated
+ // buffer, before copying it into individual code objects.
+ byte buffer[4*KB];
+
+ // Traverse the list of builtins and generate an adaptor in a
+ // separate code object for each one.
+ for (int i = 0; i < builtin_count; i++) {
+ if (create_heap_objects) {
+ MacroAssembler masm(isolate, buffer, sizeof buffer);
+ // Generate the code/adaptor.
+ typedef void (*Generator)(MacroAssembler*, int, BuiltinExtraArguments);
+ Generator g = FUNCTION_CAST<Generator>(functions[i].generator);
+ // We pass all arguments to the generator, but it may not use all of
+ // them. This works because the first arguments are on top of the
+ // stack.
+ g(&masm, functions[i].name, functions[i].extra_args);
+ // Move the code into the object heap.
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ Code::Flags flags = functions[i].flags;
+ Object* code = NULL;
+ {
+ // During startup it's OK to always allocate and defer GC to later.
+ // This simplifies things because we don't need to retry.
+ AlwaysAllocateScope __scope__;
+ { MaybeObject* maybe_code =
+ heap->CreateCode(desc, flags, masm.CodeObject());
+ if (!maybe_code->ToObject(&code)) {
+ v8::internal::V8::FatalProcessOutOfMemory("CreateCode");
+ }
+ }
+ }
+ // Log the event and add the code to the builtins array.
+ PROFILE(isolate,
+ CodeCreateEvent(Logger::BUILTIN_TAG,
+ Code::cast(code),
+ functions[i].s_name));
+ GDBJIT(AddCode(GDBJITInterface::BUILTIN,
+ functions[i].s_name,
+ Code::cast(code)));
+ builtins_[i] = code;
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_print_builtin_code) {
+ PrintF("Builtin: %s\n", functions[i].s_name);
+ Code::cast(code)->Disassemble(functions[i].s_name);
+ PrintF("\n");
+ }
+#endif
+ } else {
+ // Deserializing. The values will be filled in during IterateBuiltins.
+ builtins_[i] = NULL;
+ }
+ names_[i] = functions[i].s_name;
+ }
+
+ // Mark as initialized.
+ initialized_ = true;
+}
+
+
+void Builtins::TearDown() {
+ initialized_ = false;
+}
+
+
+void Builtins::IterateBuiltins(ObjectVisitor* v) {
+ v->VisitPointers(&builtins_[0], &builtins_[0] + builtin_count);
+}
+
+
+const char* Builtins::Lookup(byte* pc) {
+ // may be called during initialization (disassembler!)
+ if (initialized_) {
+ for (int i = 0; i < builtin_count; i++) {
+ Code* entry = Code::cast(builtins_[i]);
+ if (entry->contains(pc)) {
+ return names_[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+
+#define DEFINE_BUILTIN_ACCESSOR_C(name, ignore) \
+Handle<Code> Builtins::name() { \
+ Code** code_address = \
+ reinterpret_cast<Code**>(builtin_address(k##name)); \
+ return Handle<Code>(code_address); \
+}
+#define DEFINE_BUILTIN_ACCESSOR_A(name, kind, state, extra) \
+Handle<Code> Builtins::name() { \
+ Code** code_address = \
+ reinterpret_cast<Code**>(builtin_address(k##name)); \
+ return Handle<Code>(code_address); \
+}
+BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C)
+BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A)
+BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A)
+#undef DEFINE_BUILTIN_ACCESSOR_C
+#undef DEFINE_BUILTIN_ACCESSOR_A
+
+
+} } // namespace v8::internal