blob: 36b3177b7c02c474b058bc3f0f8f2a8c1aa5b4c1 [file] [log] [blame]
// 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 <stdlib.h>
#include <limits>
#include "src/v8.h"
#include "src/accessors.h"
#include "src/allocation-site-scopes.h"
#include "src/api.h"
#include "src/arguments.h"
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/compilation-cache.h"
#include "src/compiler.h"
#include "src/conversions.h"
#include "src/cpu.h"
#include "src/cpu-profiler.h"
#include "src/dateparser-inl.h"
#include "src/debug.h"
#include "src/deoptimizer.h"
#include "src/date.h"
#include "src/execution.h"
#include "src/full-codegen.h"
#include "src/global-handles.h"
#include "src/isolate-inl.h"
#include "src/jsregexp.h"
#include "src/jsregexp-inl.h"
#include "src/json-parser.h"
#include "src/json-stringifier.h"
#include "src/liveedit.h"
#include "src/misc-intrinsics.h"
#include "src/parser.h"
#include "src/platform.h"
#include "src/runtime-profiler.h"
#include "src/runtime.h"
#include "src/scopeinfo.h"
#include "src/smart-pointers.h"
#include "src/string-search.h"
#include "src/stub-cache.h"
#include "src/uri.h"
#include "src/v8threads.h"
#include "src/vm-state-inl.h"
#ifdef V8_I18N_SUPPORT
#include "src/i18n.h"
#include "unicode/brkiter.h"
#include "unicode/calendar.h"
#include "unicode/coll.h"
#include "unicode/curramt.h"
#include "unicode/datefmt.h"
#include "unicode/dcfmtsym.h"
#include "unicode/decimfmt.h"
#include "unicode/dtfmtsym.h"
#include "unicode/dtptngen.h"
#include "unicode/locid.h"
#include "unicode/numfmt.h"
#include "unicode/numsys.h"
#include "unicode/rbbi.h"
#include "unicode/smpdtfmt.h"
#include "unicode/timezone.h"
#include "unicode/uchar.h"
#include "unicode/ucol.h"
#include "unicode/ucurr.h"
#include "unicode/uloc.h"
#include "unicode/unum.h"
#include "unicode/uversion.h"
#endif
#ifndef _STLP_VENDOR_CSTD
// STLPort doesn't import fpclassify and isless into the std namespace.
using std::fpclassify;
using std::isless;
#endif
namespace v8 {
namespace internal {
#define RUNTIME_ASSERT(value) \
if (!(value)) return isolate->ThrowIllegalOperation();
#define RUNTIME_ASSERT_HANDLIFIED(value, T) \
if (!(value)) { \
isolate->ThrowIllegalOperation(); \
return MaybeHandle<T>(); \
}
// Cast the given object to a value of the specified type and store
// it in a variable with the given name. If the object is not of the
// expected type call IllegalOperation and return.
#define CONVERT_ARG_CHECKED(Type, name, index) \
RUNTIME_ASSERT(args[index]->Is##Type()); \
Type* name = Type::cast(args[index]);
#define CONVERT_ARG_HANDLE_CHECKED(Type, name, index) \
RUNTIME_ASSERT(args[index]->Is##Type()); \
Handle<Type> name = args.at<Type>(index);
#define CONVERT_NUMBER_ARG_HANDLE_CHECKED(name, index) \
RUNTIME_ASSERT(args[index]->IsNumber()); \
Handle<Object> name = args.at<Object>(index);
// Cast the given object to a boolean and store it in a variable with
// the given name. If the object is not a boolean call IllegalOperation
// and return.
#define CONVERT_BOOLEAN_ARG_CHECKED(name, index) \
RUNTIME_ASSERT(args[index]->IsBoolean()); \
bool name = args[index]->IsTrue();
// Cast the given argument to a Smi and store its value in an int variable
// with the given name. If the argument is not a Smi call IllegalOperation
// and return.
#define CONVERT_SMI_ARG_CHECKED(name, index) \
RUNTIME_ASSERT(args[index]->IsSmi()); \
int name = args.smi_at(index);
// Cast the given argument to a double and store it in a variable with
// the given name. If the argument is not a number (as opposed to
// the number not-a-number) call IllegalOperation and return.
#define CONVERT_DOUBLE_ARG_CHECKED(name, index) \
RUNTIME_ASSERT(args[index]->IsNumber()); \
double name = args.number_at(index);
// Call the specified converter on the object *comand store the result in
// a variable of the specified type with the given name. If the
// object is not a Number call IllegalOperation and return.
#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
RUNTIME_ASSERT(obj->IsNumber()); \
type name = NumberTo##Type(obj);
// Cast the given argument to PropertyDetails and store its value in a
// variable with the given name. If the argument is not a Smi call
// IllegalOperation and return.
#define CONVERT_PROPERTY_DETAILS_CHECKED(name, index) \
RUNTIME_ASSERT(args[index]->IsSmi()); \
PropertyDetails name = PropertyDetails(Smi::cast(args[index]));
// Assert that the given argument has a valid value for a StrictMode
// and store it in a StrictMode variable with the given name.
#define CONVERT_STRICT_MODE_ARG_CHECKED(name, index) \
RUNTIME_ASSERT(args[index]->IsSmi()); \
RUNTIME_ASSERT(args.smi_at(index) == STRICT || \
args.smi_at(index) == SLOPPY); \
StrictMode name = static_cast<StrictMode>(args.smi_at(index));
static Handle<Map> ComputeObjectLiteralMap(
Handle<Context> context,
Handle<FixedArray> constant_properties,
bool* is_result_from_cache) {
Isolate* isolate = context->GetIsolate();
int properties_length = constant_properties->length();
int number_of_properties = properties_length / 2;
// Check that there are only internal strings and array indices among keys.
int number_of_string_keys = 0;
for (int p = 0; p != properties_length; p += 2) {
Object* key = constant_properties->get(p);
uint32_t element_index = 0;
if (key->IsInternalizedString()) {
number_of_string_keys++;
} else if (key->ToArrayIndex(&element_index)) {
// An index key does not require space in the property backing store.
number_of_properties--;
} else {
// Bail out as a non-internalized-string non-index key makes caching
// impossible.
// ASSERT to make sure that the if condition after the loop is false.
ASSERT(number_of_string_keys != number_of_properties);
break;
}
}
// If we only have internalized strings and array indices among keys then we
// can use the map cache in the native context.
const int kMaxKeys = 10;
if ((number_of_string_keys == number_of_properties) &&
(number_of_string_keys < kMaxKeys)) {
// Create the fixed array with the key.
Handle<FixedArray> keys =
isolate->factory()->NewFixedArray(number_of_string_keys);
if (number_of_string_keys > 0) {
int index = 0;
for (int p = 0; p < properties_length; p += 2) {
Object* key = constant_properties->get(p);
if (key->IsInternalizedString()) {
keys->set(index++, key);
}
}
ASSERT(index == number_of_string_keys);
}
*is_result_from_cache = true;
return isolate->factory()->ObjectLiteralMapFromCache(context, keys);
}
*is_result_from_cache = false;
return Map::Create(handle(context->object_function()), number_of_properties);
}
MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
Isolate* isolate,
Handle<FixedArray> literals,
Handle<FixedArray> constant_properties);
MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate(
Isolate* isolate,
Handle<FixedArray> literals,
Handle<FixedArray> constant_properties,
bool should_have_fast_elements,
bool has_function_literal) {
// Get the native context from the literals array. This is the
// context in which the function was created and we use the object
// function from this context to create the object literal. We do
// not use the object function from the current native context
// because this might be the object function from another context
// which we should not have access to.
Handle<Context> context =
Handle<Context>(JSFunction::NativeContextFromLiterals(*literals));
// In case we have function literals, we want the object to be in
// slow properties mode for now. We don't go in the map cache because
// maps with constant functions can't be shared if the functions are
// not the same (which is the common case).
bool is_result_from_cache = false;
Handle<Map> map = has_function_literal
? Handle<Map>(context->object_function()->initial_map())
: ComputeObjectLiteralMap(context,
constant_properties,
&is_result_from_cache);
PretenureFlag pretenure_flag =
isolate->heap()->InNewSpace(*literals) ? NOT_TENURED : TENURED;
Handle<JSObject> boilerplate =
isolate->factory()->NewJSObjectFromMap(map, pretenure_flag);
// Normalize the elements of the boilerplate to save space if needed.
if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate);
// Add the constant properties to the boilerplate.
int length = constant_properties->length();
bool should_transform =
!is_result_from_cache && boilerplate->HasFastProperties();
bool should_normalize = should_transform || has_function_literal;
if (should_normalize) {
// TODO(verwaest): We might not want to ever normalize here.
JSObject::NormalizeProperties(
boilerplate, KEEP_INOBJECT_PROPERTIES, length / 2);
}
Object::ValueType value_type = should_normalize
? Object::FORCE_TAGGED : Object::OPTIMAL_REPRESENTATION;
// TODO(verwaest): Support tracking representations in the boilerplate.
for (int index = 0; index < length; index +=2) {
Handle<Object> key(constant_properties->get(index+0), isolate);
Handle<Object> value(constant_properties->get(index+1), isolate);
if (value->IsFixedArray()) {
// The value contains the constant_properties of a
// simple object or array literal.
Handle<FixedArray> array = Handle<FixedArray>::cast(value);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
CreateLiteralBoilerplate(isolate, literals, array),
Object);
}
MaybeHandle<Object> maybe_result;
uint32_t element_index = 0;
StoreMode mode = value->IsJSObject() ? FORCE_FIELD : ALLOW_AS_CONSTANT;
if (key->IsInternalizedString()) {
if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) {
// Array index as string (uint32).
maybe_result = JSObject::SetOwnElement(
boilerplate, element_index, value, SLOPPY);
} else {
Handle<String> name(String::cast(*key));
ASSERT(!name->AsArrayIndex(&element_index));
maybe_result = JSObject::SetOwnPropertyIgnoreAttributes(
boilerplate, name, value, NONE,
value_type, mode);
}
} else if (key->ToArrayIndex(&element_index)) {
// Array index (uint32).
maybe_result = JSObject::SetOwnElement(
boilerplate, element_index, value, SLOPPY);
} else {
// Non-uint32 number.
ASSERT(key->IsNumber());
double num = key->Number();
char arr[100];
Vector<char> buffer(arr, ARRAY_SIZE(arr));
const char* str = DoubleToCString(num, buffer);
Handle<String> name = isolate->factory()->NewStringFromAsciiChecked(str);
maybe_result = JSObject::SetOwnPropertyIgnoreAttributes(
boilerplate, name, value, NONE,
value_type, mode);
}
// If setting the property on the boilerplate throws an
// exception, the exception is converted to an empty handle in
// the handle based operations. In that case, we need to
// convert back to an exception.
RETURN_ON_EXCEPTION(isolate, maybe_result, Object);
}
// Transform to fast properties if necessary. For object literals with
// containing function literals we defer this operation until after all
// computed properties have been assigned so that we can generate
// constant function properties.
if (should_transform && !has_function_literal) {
JSObject::TransformToFastProperties(
boilerplate, boilerplate->map()->unused_property_fields());
}
return boilerplate;
}
MUST_USE_RESULT static MaybeHandle<Object> TransitionElements(
Handle<Object> object,
ElementsKind to_kind,
Isolate* isolate) {
HandleScope scope(isolate);
if (!object->IsJSObject()) {
isolate->ThrowIllegalOperation();
return MaybeHandle<Object>();
}
ElementsKind from_kind =
Handle<JSObject>::cast(object)->map()->elements_kind();
if (Map::IsValidElementsTransition(from_kind, to_kind)) {
JSObject::TransitionElementsKind(Handle<JSObject>::cast(object), to_kind);
return object;
}
isolate->ThrowIllegalOperation();
return MaybeHandle<Object>();
}
static const int kSmiLiteralMinimumLength = 1024;
MaybeHandle<Object> Runtime::CreateArrayLiteralBoilerplate(
Isolate* isolate,
Handle<FixedArray> literals,
Handle<FixedArray> elements) {
// Create the JSArray.
Handle<JSFunction> constructor(
JSFunction::NativeContextFromLiterals(*literals)->array_function());
PretenureFlag pretenure_flag =
isolate->heap()->InNewSpace(*literals) ? NOT_TENURED : TENURED;
Handle<JSArray> object = Handle<JSArray>::cast(
isolate->factory()->NewJSObject(constructor, pretenure_flag));
ElementsKind constant_elements_kind =
static_cast<ElementsKind>(Smi::cast(elements->get(0))->value());
Handle<FixedArrayBase> constant_elements_values(
FixedArrayBase::cast(elements->get(1)));
{ DisallowHeapAllocation no_gc;
ASSERT(IsFastElementsKind(constant_elements_kind));
Context* native_context = isolate->context()->native_context();
Object* maps_array = native_context->js_array_maps();
ASSERT(!maps_array->IsUndefined());
Object* map = FixedArray::cast(maps_array)->get(constant_elements_kind);
object->set_map(Map::cast(map));
}
Handle<FixedArrayBase> copied_elements_values;
if (IsFastDoubleElementsKind(constant_elements_kind)) {
ASSERT(FLAG_smi_only_arrays);
copied_elements_values = isolate->factory()->CopyFixedDoubleArray(
Handle<FixedDoubleArray>::cast(constant_elements_values));
} else {
ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind));
const bool is_cow =
(constant_elements_values->map() ==
isolate->heap()->fixed_cow_array_map());
if (is_cow) {
copied_elements_values = constant_elements_values;
#if DEBUG
Handle<FixedArray> fixed_array_values =
Handle<FixedArray>::cast(copied_elements_values);
for (int i = 0; i < fixed_array_values->length(); i++) {
ASSERT(!fixed_array_values->get(i)->IsFixedArray());
}
#endif
} else {
Handle<FixedArray> fixed_array_values =
Handle<FixedArray>::cast(constant_elements_values);
Handle<FixedArray> fixed_array_values_copy =
isolate->factory()->CopyFixedArray(fixed_array_values);
copied_elements_values = fixed_array_values_copy;
for (int i = 0; i < fixed_array_values->length(); i++) {
if (fixed_array_values->get(i)->IsFixedArray()) {
// The value contains the constant_properties of a
// simple object or array literal.
Handle<FixedArray> fa(FixedArray::cast(fixed_array_values->get(i)));
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
CreateLiteralBoilerplate(isolate, literals, fa),
Object);
fixed_array_values_copy->set(i, *result);
}
}
}
}
object->set_elements(*copied_elements_values);
object->set_length(Smi::FromInt(copied_elements_values->length()));
// Ensure that the boilerplate object has FAST_*_ELEMENTS, unless the flag is
// on or the object is larger than the threshold.
if (!FLAG_smi_only_arrays &&
constant_elements_values->length() < kSmiLiteralMinimumLength) {
ElementsKind elements_kind = object->GetElementsKind();
if (!IsFastObjectElementsKind(elements_kind)) {
if (IsFastHoleyElementsKind(elements_kind)) {
TransitionElements(object, FAST_HOLEY_ELEMENTS, isolate).Check();
} else {
TransitionElements(object, FAST_ELEMENTS, isolate).Check();
}
}
}
JSObject::ValidateElements(object);
return object;
}
MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
Isolate* isolate,
Handle<FixedArray> literals,
Handle<FixedArray> array) {
Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
const bool kHasNoFunctionLiteral = false;
switch (CompileTimeValue::GetLiteralType(array)) {
case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
return CreateObjectLiteralBoilerplate(isolate,
literals,
elements,
true,
kHasNoFunctionLiteral);
case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
return CreateObjectLiteralBoilerplate(isolate,
literals,
elements,
false,
kHasNoFunctionLiteral);
case CompileTimeValue::ARRAY_LITERAL:
return Runtime::CreateArrayLiteralBoilerplate(
isolate, literals, elements);
default:
UNREACHABLE();
return MaybeHandle<Object>();
}
}
RUNTIME_FUNCTION(RuntimeHidden_CreateObjectLiteral) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
CONVERT_SMI_ARG_CHECKED(literals_index, 1);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2);
CONVERT_SMI_ARG_CHECKED(flags, 3);
bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0;
RUNTIME_ASSERT(literals_index >= 0 && literals_index < literals->length());
// Check if boilerplate exists. If not, create it first.
Handle<Object> literal_site(literals->get(literals_index), isolate);
Handle<AllocationSite> site;
Handle<JSObject> boilerplate;
if (*literal_site == isolate->heap()->undefined_value()) {
Handle<Object> raw_boilerplate;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, raw_boilerplate,
CreateObjectLiteralBoilerplate(
isolate,
literals,
constant_properties,
should_have_fast_elements,
has_function_literal));
boilerplate = Handle<JSObject>::cast(raw_boilerplate);
AllocationSiteCreationContext creation_context(isolate);
site = creation_context.EnterNewScope();
RETURN_FAILURE_ON_EXCEPTION(
isolate,
JSObject::DeepWalk(boilerplate, &creation_context));
creation_context.ExitScope(site, boilerplate);
// Update the functions literal and return the boilerplate.
literals->set(literals_index, *site);
} else {
site = Handle<AllocationSite>::cast(literal_site);
boilerplate = Handle<JSObject>(JSObject::cast(site->transition_info()),
isolate);
}
AllocationSiteUsageContext usage_context(isolate, site, true);
usage_context.EnterNewScope();
MaybeHandle<Object> maybe_copy = JSObject::DeepCopy(
boilerplate, &usage_context);
usage_context.ExitScope(site, boilerplate);
Handle<Object> copy;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, copy, maybe_copy);
return *copy;
}
MUST_USE_RESULT static MaybeHandle<AllocationSite> GetLiteralAllocationSite(
Isolate* isolate,
Handle<FixedArray> literals,
int literals_index,
Handle<FixedArray> elements) {
// Check if boilerplate exists. If not, create it first.
Handle<Object> literal_site(literals->get(literals_index), isolate);
Handle<AllocationSite> site;
if (*literal_site == isolate->heap()->undefined_value()) {
ASSERT(*elements != isolate->heap()->empty_fixed_array());
Handle<Object> boilerplate;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, boilerplate,
Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements),
AllocationSite);
AllocationSiteCreationContext creation_context(isolate);
site = creation_context.EnterNewScope();
if (JSObject::DeepWalk(Handle<JSObject>::cast(boilerplate),
&creation_context).is_null()) {
return Handle<AllocationSite>::null();
}
creation_context.ExitScope(site, Handle<JSObject>::cast(boilerplate));
literals->set(literals_index, *site);
} else {
site = Handle<AllocationSite>::cast(literal_site);
}
return site;
}
static MaybeHandle<JSObject> CreateArrayLiteralImpl(Isolate* isolate,
Handle<FixedArray> literals,
int literals_index,
Handle<FixedArray> elements,
int flags) {
RUNTIME_ASSERT_HANDLIFIED(literals_index >= 0 &&
literals_index < literals->length(), JSObject);
Handle<AllocationSite> site;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, site,
GetLiteralAllocationSite(isolate, literals, literals_index, elements),
JSObject);
bool enable_mementos = (flags & ArrayLiteral::kDisableMementos) == 0;
Handle<JSObject> boilerplate(JSObject::cast(site->transition_info()));
AllocationSiteUsageContext usage_context(isolate, site, enable_mementos);
usage_context.EnterNewScope();
JSObject::DeepCopyHints hints = (flags & ArrayLiteral::kShallowElements) == 0
? JSObject::kNoHints
: JSObject::kObjectIsShallowArray;
MaybeHandle<JSObject> copy = JSObject::DeepCopy(boilerplate, &usage_context,
hints);
usage_context.ExitScope(site, boilerplate);
return copy;
}
RUNTIME_FUNCTION(RuntimeHidden_CreateArrayLiteral) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
CONVERT_SMI_ARG_CHECKED(literals_index, 1);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2);
CONVERT_SMI_ARG_CHECKED(flags, 3);
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
CreateArrayLiteralImpl(isolate, literals, literals_index, elements,
flags));
return *result;
}
RUNTIME_FUNCTION(RuntimeHidden_CreateArrayLiteralStubBailout) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
CONVERT_SMI_ARG_CHECKED(literals_index, 1);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2);
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
CreateArrayLiteralImpl(isolate, literals, literals_index, elements,
ArrayLiteral::kShallowElements));
return *result;
}
RUNTIME_FUNCTION(Runtime_CreateSymbol) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
Handle<Symbol> symbol = isolate->factory()->NewSymbol();
if (name->IsString()) symbol->set_name(*name);
return *symbol;
}
RUNTIME_FUNCTION(Runtime_CreatePrivateSymbol) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
Handle<Symbol> symbol = isolate->factory()->NewPrivateSymbol();
if (name->IsString()) symbol->set_name(*name);
return *symbol;
}
RUNTIME_FUNCTION(Runtime_CreateGlobalPrivateSymbol) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
Handle<JSObject> registry = isolate->GetSymbolRegistry();
Handle<String> part = isolate->factory()->private_intern_string();
Handle<Object> privates;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, privates, Object::GetPropertyOrElement(registry, part));
Handle<Object> symbol;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, symbol, Object::GetPropertyOrElement(privates, name));
if (!symbol->IsSymbol()) {
ASSERT(symbol->IsUndefined());
symbol = isolate->factory()->NewPrivateSymbol();
Handle<Symbol>::cast(symbol)->set_name(*name);
JSObject::SetProperty(Handle<JSObject>::cast(privates),
name, symbol, NONE, STRICT).Assert();
}
return *symbol;
}
RUNTIME_FUNCTION(Runtime_NewSymbolWrapper) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Symbol, symbol, 0);
return *Object::ToObject(isolate, symbol).ToHandleChecked();
}
RUNTIME_FUNCTION(Runtime_SymbolDescription) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(Symbol, symbol, 0);
return symbol->name();
}
RUNTIME_FUNCTION(Runtime_SymbolRegistry) {
HandleScope scope(isolate);
ASSERT(args.length() == 0);
return *isolate->GetSymbolRegistry();
}
RUNTIME_FUNCTION(Runtime_SymbolIsPrivate) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(Symbol, symbol, 0);
return isolate->heap()->ToBoolean(symbol->is_private());
}
RUNTIME_FUNCTION(Runtime_CreateJSProxy) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, handler, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
if (!prototype->IsJSReceiver()) prototype = isolate->factory()->null_value();
return *isolate->factory()->NewJSProxy(handler, prototype);
}
RUNTIME_FUNCTION(Runtime_CreateJSFunctionProxy) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, handler, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, call_trap, 1);
RUNTIME_ASSERT(call_trap->IsJSFunction() || call_trap->IsJSFunctionProxy());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, construct_trap, 2);
CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 3);
if (!prototype->IsJSReceiver()) prototype = isolate->factory()->null_value();
return *isolate->factory()->NewJSFunctionProxy(
handler, call_trap, construct_trap, prototype);
}
RUNTIME_FUNCTION(Runtime_IsJSProxy) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
return isolate->heap()->ToBoolean(obj->IsJSProxy());
}
RUNTIME_FUNCTION(Runtime_IsJSFunctionProxy) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
return isolate->heap()->ToBoolean(obj->IsJSFunctionProxy());
}
RUNTIME_FUNCTION(Runtime_GetHandler) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSProxy, proxy, 0);
return proxy->handler();
}
RUNTIME_FUNCTION(Runtime_GetCallTrap) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSFunctionProxy, proxy, 0);
return proxy->call_trap();
}
RUNTIME_FUNCTION(Runtime_GetConstructTrap) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSFunctionProxy, proxy, 0);
return proxy->construct_trap();
}
RUNTIME_FUNCTION(Runtime_Fix) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSProxy, proxy, 0);
JSProxy::Fix(proxy);
return isolate->heap()->undefined_value();
}
void Runtime::FreeArrayBuffer(Isolate* isolate,
JSArrayBuffer* phantom_array_buffer) {
if (phantom_array_buffer->should_be_freed()) {
ASSERT(phantom_array_buffer->is_external());
free(phantom_array_buffer->backing_store());
}
if (phantom_array_buffer->is_external()) return;
size_t allocated_length = NumberToSize(
isolate, phantom_array_buffer->byte_length());
reinterpret_cast<v8::Isolate*>(isolate)
->AdjustAmountOfExternalAllocatedMemory(
-static_cast<int64_t>(allocated_length));
CHECK(V8::ArrayBufferAllocator() != NULL);
V8::ArrayBufferAllocator()->Free(
phantom_array_buffer->backing_store(),
allocated_length);
}
void Runtime::SetupArrayBuffer(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer,
bool is_external,
void* data,
size_t allocated_length) {
ASSERT(array_buffer->GetInternalFieldCount() ==
v8::ArrayBuffer::kInternalFieldCount);
for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
array_buffer->SetInternalField(i, Smi::FromInt(0));
}
array_buffer->set_backing_store(data);
array_buffer->set_flag(Smi::FromInt(0));
array_buffer->set_is_external(is_external);
Handle<Object> byte_length =
isolate->factory()->NewNumberFromSize(allocated_length);
CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
array_buffer->set_byte_length(*byte_length);
array_buffer->set_weak_next(isolate->heap()->array_buffers_list());
isolate->heap()->set_array_buffers_list(*array_buffer);
array_buffer->set_weak_first_view(isolate->heap()->undefined_value());
}
bool Runtime::SetupArrayBufferAllocatingData(
Isolate* isolate,
Handle<JSArrayBuffer> array_buffer,
size_t allocated_length,
bool initialize) {
void* data;
CHECK(V8::ArrayBufferAllocator() != NULL);
if (allocated_length != 0) {
if (initialize) {
data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
} else {
data =
V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length);
}
if (data == NULL) return false;
} else {
data = NULL;
}
SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
reinterpret_cast<v8::Isolate*>(isolate)
->AdjustAmountOfExternalAllocatedMemory(allocated_length);
return true;
}
void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) {
Isolate* isolate = array_buffer->GetIsolate();
for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate);
!view_obj->IsUndefined();) {
Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj));
if (view->IsJSTypedArray()) {
JSTypedArray::cast(*view)->Neuter();
} else if (view->IsJSDataView()) {
JSDataView::cast(*view)->Neuter();
} else {
UNREACHABLE();
}
view_obj = handle(view->weak_next(), isolate);
}
array_buffer->Neuter();
}
RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1);
if (!holder->byte_length()->IsUndefined()) {
// ArrayBuffer is already initialized; probably a fuzz test.
return *holder;
}
size_t allocated_length = 0;
if (!TryNumberToSize(isolate, *byteLength, &allocated_length)) {
return isolate->Throw(
*isolate->factory()->NewRangeError("invalid_array_buffer_length",
HandleVector<Object>(NULL, 0)));
}
if (!Runtime::SetupArrayBufferAllocatingData(isolate,
holder, allocated_length)) {
return isolate->Throw(
*isolate->factory()->NewRangeError("invalid_array_buffer_length",
HandleVector<Object>(NULL, 0)));
}
return *holder;
}
RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
return holder->byte_length();
}
RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
RUNTIME_ASSERT(!source.is_identical_to(target));
size_t start = 0;
RUNTIME_ASSERT(TryNumberToSize(isolate, *first, &start));
size_t target_length = NumberToSize(isolate, target->byte_length());
if (target_length == 0) return isolate->heap()->undefined_value();
size_t source_byte_length = NumberToSize(isolate, source->byte_length());
RUNTIME_ASSERT(start <= source_byte_length);
RUNTIME_ASSERT(source_byte_length - start >= target_length);
uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
CopyBytes(target_data, source_data + start, target_length);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_ArrayBufferIsView) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(Object, object, 0);
return isolate->heap()->ToBoolean(object->IsJSArrayBufferView());
}
RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
if (array_buffer->backing_store() == NULL) {
CHECK(Smi::FromInt(0) == array_buffer->byte_length());
return isolate->heap()->undefined_value();
}
ASSERT(!array_buffer->is_external());
void* backing_store = array_buffer->backing_store();
size_t byte_length = NumberToSize(isolate, array_buffer->byte_length());
array_buffer->set_is_external(true);
Runtime::NeuterArrayBuffer(array_buffer);
V8::ArrayBufferAllocator()->Free(backing_store, byte_length);
return isolate->heap()->undefined_value();
}
void Runtime::ArrayIdToTypeAndSize(
int arrayId,
ExternalArrayType* array_type,
ElementsKind* external_elements_kind,
ElementsKind* fixed_elements_kind,
size_t* element_size) {
switch (arrayId) {
#define ARRAY_ID_CASE(Type, type, TYPE, ctype, size) \
case ARRAY_ID_##TYPE: \
*array_type = kExternal##Type##Array; \
*external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \
*fixed_elements_kind = TYPE##_ELEMENTS; \
*element_size = size; \
break;
TYPED_ARRAYS(ARRAY_ID_CASE)
#undef ARRAY_ID_CASE
default:
UNREACHABLE();
}
}
RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 5);
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
CONVERT_SMI_ARG_CHECKED(arrayId, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4);
RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
arrayId <= Runtime::ARRAY_ID_LAST);
ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
size_t element_size = 1; // Bogus initialization.
ElementsKind external_elements_kind =
EXTERNAL_INT8_ELEMENTS; // Bogus initialization.
ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
Runtime::ArrayIdToTypeAndSize(arrayId,
&array_type,
&external_elements_kind,
&fixed_elements_kind,
&element_size);
RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
size_t byte_offset = 0;
size_t byte_length = 0;
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset_object, &byte_offset));
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length_object, &byte_length));
if (maybe_buffer->IsJSArrayBuffer()) {
Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
size_t array_buffer_byte_length =
NumberToSize(isolate, buffer->byte_length());
RUNTIME_ASSERT(byte_offset <= array_buffer_byte_length);
RUNTIME_ASSERT(array_buffer_byte_length - byte_offset >= byte_length);
} else {
RUNTIME_ASSERT(maybe_buffer->IsNull());
}
RUNTIME_ASSERT(byte_length % element_size == 0);
size_t length = byte_length / element_size;
if (length > static_cast<unsigned>(Smi::kMaxValue)) {
return isolate->Throw(
*isolate->factory()->NewRangeError("invalid_typed_array_length",
HandleVector<Object>(NULL, 0)));
}
// All checks are done, now we can modify objects.
ASSERT(holder->GetInternalFieldCount() ==
v8::ArrayBufferView::kInternalFieldCount);
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
holder->SetInternalField(i, Smi::FromInt(0));
}
Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
holder->set_length(*length_obj);
holder->set_byte_offset(*byte_offset_object);
holder->set_byte_length(*byte_length_object);
if (!maybe_buffer->IsNull()) {
Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
holder->set_buffer(*buffer);
holder->set_weak_next(buffer->weak_first_view());
buffer->set_weak_first_view(*holder);
Handle<ExternalArray> elements =
isolate->factory()->NewExternalArray(
static_cast<int>(length), array_type,
static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
Handle<Map> map =
JSObject::GetElementsTransitionMap(holder, external_elements_kind);
JSObject::SetMapAndElements(holder, map, elements);
ASSERT(IsExternalArrayElementsKind(holder->map()->elements_kind()));
} else {
holder->set_buffer(Smi::FromInt(0));
holder->set_weak_next(isolate->heap()->undefined_value());
Handle<FixedTypedArrayBase> elements =
isolate->factory()->NewFixedTypedArray(
static_cast<int>(length), array_type);
holder->set_elements(*elements);
}
return isolate->heap()->undefined_value();
}
// Initializes a typed array from an array-like object.
// If an array-like object happens to be a typed array of the same type,
// initializes backing store using memove.
//
// Returns true if backing store was initialized or false otherwise.
RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
CONVERT_SMI_ARG_CHECKED(arrayId, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3);
RUNTIME_ASSERT(arrayId >= Runtime::ARRAY_ID_FIRST &&
arrayId <= Runtime::ARRAY_ID_LAST);
ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
size_t element_size = 1; // Bogus initialization.
ElementsKind external_elements_kind =
EXTERNAL_INT8_ELEMENTS; // Bogus intialization.
ElementsKind fixed_elements_kind = INT8_ELEMENTS; // Bogus initialization.
Runtime::ArrayIdToTypeAndSize(arrayId,
&array_type,
&external_elements_kind,
&fixed_elements_kind,
&element_size);
RUNTIME_ASSERT(holder->map()->elements_kind() == fixed_elements_kind);
Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
if (source->IsJSTypedArray() &&
JSTypedArray::cast(*source)->type() == array_type) {
length_obj = Handle<Object>(JSTypedArray::cast(*source)->length(), isolate);
}
size_t length = 0;
RUNTIME_ASSERT(TryNumberToSize(isolate, *length_obj, &length));
if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
(length > (kMaxInt / element_size))) {
return isolate->Throw(*isolate->factory()->
NewRangeError("invalid_typed_array_length",
HandleVector<Object>(NULL, 0)));
}
size_t byte_length = length * element_size;
ASSERT(holder->GetInternalFieldCount() ==
v8::ArrayBufferView::kInternalFieldCount);
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
holder->SetInternalField(i, Smi::FromInt(0));
}
// NOTE: not initializing backing store.
// We assume that the caller of this function will initialize holder
// with the loop
// for(i = 0; i < length; i++) { holder[i] = source[i]; }
// We assume that the caller of this function is always a typed array
// constructor.
// If source is a typed array, this loop will always run to completion,
// so we are sure that the backing store will be initialized.
// Otherwise, the indexing operation might throw, so the loop will not
// run to completion and the typed array might remain partly initialized.
// However we further assume that the caller of this function is a typed array
// constructor, and the exception will propagate out of the constructor,
// therefore uninitialized memory will not be accessible by a user program.
//
// TODO(dslomov): revise this once we support subclassing.
if (!Runtime::SetupArrayBufferAllocatingData(
isolate, buffer, byte_length, false)) {
return isolate->Throw(*isolate->factory()->
NewRangeError("invalid_array_buffer_length",
HandleVector<Object>(NULL, 0)));
}
holder->set_buffer(*buffer);
holder->set_byte_offset(Smi::FromInt(0));
Handle<Object> byte_length_obj(
isolate->factory()->NewNumberFromSize(byte_length));
holder->set_byte_length(*byte_length_obj);
holder->set_length(*length_obj);
holder->set_weak_next(buffer->weak_first_view());
buffer->set_weak_first_view(*holder);
Handle<ExternalArray> elements =
isolate->factory()->NewExternalArray(
static_cast<int>(length), array_type,
static_cast<uint8_t*>(buffer->backing_store()));
Handle<Map> map = JSObject::GetElementsTransitionMap(
holder, external_elements_kind);
JSObject::SetMapAndElements(holder, map, elements);
if (source->IsJSTypedArray()) {
Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
if (typed_array->type() == holder->type()) {
uint8_t* backing_store =
static_cast<uint8_t*>(
typed_array->GetBuffer()->backing_store());
size_t source_byte_offset =
NumberToSize(isolate, typed_array->byte_offset());
memcpy(
buffer->backing_store(),
backing_store + source_byte_offset,
byte_length);
return isolate->heap()->true_value();
}
}
return isolate->heap()->false_value();
}
#define BUFFER_VIEW_GETTER(Type, getter, accessor) \
RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \
HandleScope scope(isolate); \
ASSERT(args.length() == 1); \
CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
return holder->accessor(); \
}
BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
BUFFER_VIEW_GETTER(TypedArray, Length, length)
BUFFER_VIEW_GETTER(DataView, Buffer, buffer)
#undef BUFFER_VIEW_GETTER
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
return *holder->GetBuffer();
}
// Return codes for Runtime_TypedArraySetFastCases.
// Should be synchronized with typedarray.js natives.
enum TypedArraySetResultCodes {
// Set from typed array of the same type.
// This is processed by TypedArraySetFastCases
TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
// Set from typed array of the different type, overlapping in memory.
TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
// Set from typed array of the different type, non-overlapping.
TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
// Set from non-typed array.
TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
};
RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
if (!args[0]->IsJSTypedArray())
return isolate->Throw(*isolate->factory()->NewTypeError(
"not_typed_array", HandleVector<Object>(NULL, 0)));
if (!args[1]->IsJSTypedArray())
return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0);
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2);
Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
size_t offset = 0;
RUNTIME_ASSERT(TryNumberToSize(isolate, *offset_obj, &offset));
size_t target_length = NumberToSize(isolate, target->length());
size_t source_length = NumberToSize(isolate, source->length());
size_t target_byte_length = NumberToSize(isolate, target->byte_length());
size_t source_byte_length = NumberToSize(isolate, source->byte_length());
if (offset > target_length ||
offset + source_length > target_length ||
offset + source_length < offset) // overflow
return isolate->Throw(*isolate->factory()->NewRangeError(
"typed_array_set_source_too_large", HandleVector<Object>(NULL, 0)));
size_t target_offset = NumberToSize(isolate, target->byte_offset());
size_t source_offset = NumberToSize(isolate, source->byte_offset());
uint8_t* target_base =
static_cast<uint8_t*>(
target->GetBuffer()->backing_store()) + target_offset;
uint8_t* source_base =
static_cast<uint8_t*>(
source->GetBuffer()->backing_store()) + source_offset;
// Typed arrays of the same type: use memmove.
if (target->type() == source->type()) {
memmove(target_base + offset * target->element_size(),
source_base, source_byte_length);
return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
}
// Typed arrays of different types over the same backing store
if ((source_base <= target_base &&
source_base + source_byte_length > target_base) ||
(target_base <= source_base &&
target_base + target_byte_length > source_base)) {
// We do not support overlapping ArrayBuffers
ASSERT(
target->GetBuffer()->backing_store() ==
source->GetBuffer()->backing_store());
return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
} else { // Non-overlapping typed arrays
return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
}
}
RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) {
ASSERT(args.length() == 0);
ASSERT_OBJECT_SIZE(
FLAG_typed_array_max_size_in_heap + FixedTypedArrayBase::kDataOffset);
return Smi::FromInt(FLAG_typed_array_max_size_in_heap);
}
RUNTIME_FUNCTION(Runtime_DataViewInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length, 3);
ASSERT(holder->GetInternalFieldCount() ==
v8::ArrayBufferView::kInternalFieldCount);
for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
holder->SetInternalField(i, Smi::FromInt(0));
}
size_t buffer_length = 0;
size_t offset = 0;
size_t length = 0;
RUNTIME_ASSERT(
TryNumberToSize(isolate, buffer->byte_length(), &buffer_length));
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_offset, &offset));
RUNTIME_ASSERT(TryNumberToSize(isolate, *byte_length, &length));
// TODO(jkummerow): When we have a "safe numerics" helper class, use it here.
// Entire range [offset, offset + length] must be in bounds.
RUNTIME_ASSERT(offset <= buffer_length);
RUNTIME_ASSERT(offset + length <= buffer_length);
// No overflow.
RUNTIME_ASSERT(offset + length >= offset);
holder->set_buffer(*buffer);
holder->set_byte_offset(*byte_offset);
holder->set_byte_length(*byte_length);
holder->set_weak_next(buffer->weak_first_view());
buffer->set_weak_first_view(*holder);
return isolate->heap()->undefined_value();
}
inline static bool NeedToFlipBytes(bool is_little_endian) {
#ifdef V8_TARGET_LITTLE_ENDIAN
return !is_little_endian;
#else
return is_little_endian;
#endif
}
template<int n>
inline void CopyBytes(uint8_t* target, uint8_t* source) {
for (int i = 0; i < n; i++) {
*(target++) = *(source++);
}
}
template<int n>
inline void FlipBytes(uint8_t* target, uint8_t* source) {
source = source + (n-1);
for (int i = 0; i < n; i++) {
*(target++) = *(source--);
}
}
template<typename T>
inline static bool DataViewGetValue(
Isolate* isolate,
Handle<JSDataView> data_view,
Handle<Object> byte_offset_obj,
bool is_little_endian,
T* result) {
size_t byte_offset = 0;
if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
return false;
}
Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
size_t data_view_byte_offset =
NumberToSize(isolate, data_view->byte_offset());
size_t data_view_byte_length =
NumberToSize(isolate, data_view->byte_length());
if (byte_offset + sizeof(T) > data_view_byte_length ||
byte_offset + sizeof(T) < byte_offset) { // overflow
return false;
}
union Value {
T data;
uint8_t bytes[sizeof(T)];
};
Value value;
size_t buffer_offset = data_view_byte_offset + byte_offset;
ASSERT(
NumberToSize(isolate, buffer->byte_length())
>= buffer_offset + sizeof(T));
uint8_t* source =
static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
if (NeedToFlipBytes(is_little_endian)) {
FlipBytes<sizeof(T)>(value.bytes, source);
} else {
CopyBytes<sizeof(T)>(value.bytes, source);
}
*result = value.data;
return true;
}
template<typename T>
static bool DataViewSetValue(
Isolate* isolate,
Handle<JSDataView> data_view,
Handle<Object> byte_offset_obj,
bool is_little_endian,
T data) {
size_t byte_offset = 0;
if (!TryNumberToSize(isolate, *byte_offset_obj, &byte_offset)) {
return false;
}
Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
size_t data_view_byte_offset =
NumberToSize(isolate, data_view->byte_offset());
size_t data_view_byte_length =
NumberToSize(isolate, data_view->byte_length());
if (byte_offset + sizeof(T) > data_view_byte_length ||
byte_offset + sizeof(T) < byte_offset) { // overflow
return false;
}
union Value {
T data;
uint8_t bytes[sizeof(T)];
};
Value value;
value.data = data;
size_t buffer_offset = data_view_byte_offset + byte_offset;
ASSERT(
NumberToSize(isolate, buffer->byte_length())
>= buffer_offset + sizeof(T));
uint8_t* target =
static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
if (NeedToFlipBytes(is_little_endian)) {
FlipBytes<sizeof(T)>(target, value.bytes);
} else {
CopyBytes<sizeof(T)>(target, value.bytes);
}
return true;
}
#define DATA_VIEW_GETTER(TypeName, Type, Converter) \
RUNTIME_FUNCTION(Runtime_DataViewGet##TypeName) { \
HandleScope scope(isolate); \
ASSERT(args.length() == 3); \
CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2); \
Type result; \
if (DataViewGetValue( \
isolate, holder, offset, is_little_endian, &result)) { \
return *isolate->factory()->Converter(result); \
} else { \
return isolate->Throw(*isolate->factory()->NewRangeError( \
"invalid_data_view_accessor_offset", \
HandleVector<Object>(NULL, 0))); \
} \
}
DATA_VIEW_GETTER(Uint8, uint8_t, NewNumberFromUint)
DATA_VIEW_GETTER(Int8, int8_t, NewNumberFromInt)
DATA_VIEW_GETTER(Uint16, uint16_t, NewNumberFromUint)
DATA_VIEW_GETTER(Int16, int16_t, NewNumberFromInt)
DATA_VIEW_GETTER(Uint32, uint32_t, NewNumberFromUint)
DATA_VIEW_GETTER(Int32, int32_t, NewNumberFromInt)
DATA_VIEW_GETTER(Float32, float, NewNumber)
DATA_VIEW_GETTER(Float64, double, NewNumber)
#undef DATA_VIEW_GETTER
template <typename T>
static T DataViewConvertValue(double value);
template <>
int8_t DataViewConvertValue<int8_t>(double value) {
return static_cast<int8_t>(DoubleToInt32(value));
}
template <>
int16_t DataViewConvertValue<int16_t>(double value) {
return static_cast<int16_t>(DoubleToInt32(value));
}
template <>
int32_t DataViewConvertValue<int32_t>(double value) {
return DoubleToInt32(value);
}
template <>
uint8_t DataViewConvertValue<uint8_t>(double value) {
return static_cast<uint8_t>(DoubleToUint32(value));
}
template <>
uint16_t DataViewConvertValue<uint16_t>(double value) {
return static_cast<uint16_t>(DoubleToUint32(value));
}
template <>
uint32_t DataViewConvertValue<uint32_t>(double value) {
return DoubleToUint32(value);
}
template <>
float DataViewConvertValue<float>(double value) {
return static_cast<float>(value);
}
template <>
double DataViewConvertValue<double>(double value) {
return value;
}
#define DATA_VIEW_SETTER(TypeName, Type) \
RUNTIME_FUNCTION(Runtime_DataViewSet##TypeName) { \
HandleScope scope(isolate); \
ASSERT(args.length() == 4); \
CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2); \
CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3); \
Type v = DataViewConvertValue<Type>(value->Number()); \
if (DataViewSetValue( \
isolate, holder, offset, is_little_endian, v)) { \
return isolate->heap()->undefined_value(); \
} else { \
return isolate->Throw(*isolate->factory()->NewRangeError( \
"invalid_data_view_accessor_offset", \
HandleVector<Object>(NULL, 0))); \
} \
}
DATA_VIEW_SETTER(Uint8, uint8_t)
DATA_VIEW_SETTER(Int8, int8_t)
DATA_VIEW_SETTER(Uint16, uint16_t)
DATA_VIEW_SETTER(Int16, int16_t)
DATA_VIEW_SETTER(Uint32, uint32_t)
DATA_VIEW_SETTER(Int32, int32_t)
DATA_VIEW_SETTER(Float32, float)
DATA_VIEW_SETTER(Float64, double)
#undef DATA_VIEW_SETTER
RUNTIME_FUNCTION(Runtime_SetInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
Handle<OrderedHashSet> table = isolate->factory()->NewOrderedHashSet();
holder->set_table(*table);
return *holder;
}
RUNTIME_FUNCTION(Runtime_SetAdd) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
table = OrderedHashSet::Add(table, key);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetHas) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
return isolate->heap()->ToBoolean(table->Contains(key));
}
RUNTIME_FUNCTION(Runtime_SetDelete) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
bool was_present = false;
table = OrderedHashSet::Remove(table, key, &was_present);
holder->set_table(*table);
return isolate->heap()->ToBoolean(was_present);
}
RUNTIME_FUNCTION(Runtime_SetClear) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
table = OrderedHashSet::Clear(table);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetGetSize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
return Smi::FromInt(table->NumberOfElements());
}
RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(JSSet, set, 1);
CONVERT_SMI_ARG_CHECKED(kind, 2)
RUNTIME_ASSERT(kind == JSSetIterator::kKindValues ||
kind == JSSetIterator::kKindEntries);
Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
holder->set_table(*table);
holder->set_index(Smi::FromInt(0));
holder->set_kind(Smi::FromInt(kind));
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
return *JSSetIterator::Next(holder);
}
RUNTIME_FUNCTION(Runtime_MapInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
holder->set_table(*table);
return *holder;
}
RUNTIME_FUNCTION(Runtime_MapGet) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<Object> lookup(table->Lookup(key), isolate);
return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
}
RUNTIME_FUNCTION(Runtime_MapHas) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<Object> lookup(table->Lookup(key), isolate);
return isolate->heap()->ToBoolean(!lookup->IsTheHole());
}
RUNTIME_FUNCTION(Runtime_MapDelete) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
bool was_present = false;
Handle<OrderedHashMap> new_table =
OrderedHashMap::Remove(table, key, &was_present);
holder->set_table(*new_table);
return isolate->heap()->ToBoolean(was_present);
}
RUNTIME_FUNCTION(Runtime_MapClear) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
table = OrderedHashMap::Clear(table);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_MapSet) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<OrderedHashMap> new_table = OrderedHashMap::Put(table, key, value);
holder->set_table(*new_table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_MapGetSize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
return Smi::FromInt(table->NumberOfElements());
}
RUNTIME_FUNCTION(Runtime_MapIteratorInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(JSMap, map, 1);
CONVERT_SMI_ARG_CHECKED(kind, 2)
RUNTIME_ASSERT(kind == JSMapIterator::kKindKeys
|| kind == JSMapIterator::kKindValues
|| kind == JSMapIterator::kKindEntries);
Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()));
holder->set_table(*table);
holder->set_index(Smi::FromInt(0));
holder->set_kind(Smi::FromInt(kind));
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_MapIteratorNext) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
return *JSMapIterator::Next(holder);
}
static Handle<JSWeakCollection> WeakCollectionInitialize(
Isolate* isolate,
Handle<JSWeakCollection> weak_collection) {
ASSERT(weak_collection->map()->inobject_properties() == 0);
Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 0);
weak_collection->set_table(*table);
return weak_collection;
}
RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
return *WeakCollectionInitialize(isolate, weak_collection);
}
RUNTIME_FUNCTION(Runtime_WeakCollectionGet) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
RUNTIME_ASSERT(table->IsKey(*key));
Handle<Object> lookup(table->Lookup(key), isolate);
return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
}
RUNTIME_FUNCTION(Runtime_WeakCollectionHas) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
RUNTIME_ASSERT(table->IsKey(*key));
Handle<Object> lookup(table->Lookup(key), isolate);
return isolate->heap()->ToBoolean(!lookup->IsTheHole());
}
RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
Handle<ObjectHashTable> table(ObjectHashTable::cast(
weak_collection->table()));
RUNTIME_ASSERT(table->IsKey(*key));
bool was_present = false;
Handle<ObjectHashTable> new_table =
ObjectHashTable::Remove(table, key, &was_present);
weak_collection->set_table(*new_table);
return isolate->heap()->ToBoolean(was_present);
}
RUNTIME_FUNCTION(Runtime_WeakCollectionSet) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol());
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
RUNTIME_ASSERT(table->IsKey(*key));
Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
weak_collection->set_table(*new_table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_ClassOf) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(Object, obj, 0);
if (!obj->IsJSObject()) return isolate->heap()->null_value();
return JSObject::cast(obj)->class_name();
}
RUNTIME_FUNCTION(Runtime_GetPrototype) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
// We don't expect access checks to be needed on JSProxy objects.
ASSERT(!obj->IsAccessCheckNeeded() || obj->IsJSObject());
do {
if (obj->IsAccessCheckNeeded() &&
!isolate->MayNamedAccess(Handle<JSObject>::cast(obj),
isolate->factory()->proto_string(),
v8::ACCESS_GET)) {
isolate->ReportFailedAccessCheck(Handle<JSObject>::cast(obj),
v8::ACCESS_GET);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->undefined_value();
}
obj = Object::GetPrototype(isolate, obj);
} while (obj->IsJSObject() &&
JSObject::cast(*obj)->map()->is_hidden_prototype());
return *obj;
}
static inline Handle<Object> GetPrototypeSkipHiddenPrototypes(
Isolate* isolate, Handle<Object> receiver) {
Handle<Object> current = Object::GetPrototype(isolate, receiver);
while (current->IsJSObject() &&
JSObject::cast(*current)->map()->is_hidden_prototype()) {
current = Object::GetPrototype(isolate, current);
}
return current;
}
RUNTIME_FUNCTION(Runtime_SetPrototype) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
if (obj->IsAccessCheckNeeded() &&
!isolate->MayNamedAccess(
obj, isolate->factory()->proto_string(), v8::ACCESS_SET)) {
isolate->ReportFailedAccessCheck(obj, v8::ACCESS_SET);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->undefined_value();
}
if (obj->map()->is_observed()) {
Handle<Object> old_value = GetPrototypeSkipHiddenPrototypes(isolate, obj);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
JSObject::SetPrototype(obj, prototype, true));
Handle<Object> new_value = GetPrototypeSkipHiddenPrototypes(isolate, obj);
if (!new_value->SameValue(*old_value)) {
JSObject::EnqueueChangeRecord(obj, "setPrototype",
isolate->factory()->proto_string(),
old_value);
}
return *result;
}
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
JSObject::SetPrototype(obj, prototype, true));
return *result;
}
RUNTIME_FUNCTION(Runtime_IsInPrototypeChain) {
HandleScope shs(isolate);
ASSERT(args.length() == 2);
// See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
CONVERT_ARG_HANDLE_CHECKED(Object, O, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, V, 1);
while (true) {
Handle<Object> prototype = Object::GetPrototype(isolate, V);
if (prototype->IsNull()) return isolate->heap()->false_value();
if (*O == *prototype) return isolate->heap()->true_value();
V = prototype;
}
}
static bool CheckAccessException(Object* callback,
v8::AccessType access_type) {
DisallowHeapAllocation no_gc;
ASSERT(!callback->IsForeign());
if (callback->IsAccessorInfo()) {
AccessorInfo* info = AccessorInfo::cast(callback);
return
(access_type == v8::ACCESS_HAS &&
(info->all_can_read() || info->all_can_write())) ||
(access_type == v8::ACCESS_GET && info->all_can_read()) ||
(access_type == v8::ACCESS_SET && info->all_can_write());
}
if (callback->IsAccessorPair()) {
AccessorPair* info = AccessorPair::cast(callback);
return
(access_type == v8::ACCESS_HAS &&
(info->all_can_read() || info->all_can_write())) ||
(access_type == v8::ACCESS_GET && info->all_can_read()) ||
(access_type == v8::ACCESS_SET && info->all_can_write());
}
return false;
}
template<class Key>
static bool CheckGenericAccess(
Handle<JSObject> receiver,
Handle<JSObject> holder,
Key key,
v8::AccessType access_type,
bool (Isolate::*mayAccess)(Handle<JSObject>, Key, v8::AccessType)) {
Isolate* isolate = receiver->GetIsolate();
for (Handle<JSObject> current = receiver;
true;
current = handle(JSObject::cast(current->GetPrototype()), isolate)) {
if (current->IsAccessCheckNeeded() &&
!(isolate->*mayAccess)(current, key, access_type)) {
return false;
}
if (current.is_identical_to(holder)) break;
}
return true;
}
enum AccessCheckResult {
ACCESS_FORBIDDEN,
ACCESS_ALLOWED,
ACCESS_ABSENT
};
static AccessCheckResult CheckPropertyAccess(Handle<JSObject> obj,
Handle<Name> name,
v8::AccessType access_type) {
uint32_t index;
if (name->AsArrayIndex(&index)) {
// TODO(1095): we should traverse hidden prototype hierachy as well.
if (CheckGenericAccess(
obj, obj, index, access_type, &Isolate::MayIndexedAccess)) {
return ACCESS_ALLOWED;
}
obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type);
return ACCESS_FORBIDDEN;
}
Isolate* isolate = obj->GetIsolate();
LookupResult lookup(isolate);
obj->LookupOwn(name, &lookup, true);
if (!lookup.IsProperty()) return ACCESS_ABSENT;
Handle<JSObject> holder(lookup.holder(), isolate);
if (CheckGenericAccess<Handle<Object> >(
obj, holder, name, access_type, &Isolate::MayNamedAccess)) {
return ACCESS_ALLOWED;
}
// Access check callback denied the access, but some properties
// can have a special permissions which override callbacks descision
// (currently see v8::AccessControl).
// API callbacks can have per callback access exceptions.
switch (lookup.type()) {
case CALLBACKS:
if (CheckAccessException(lookup.GetCallbackObject(), access_type)) {
return ACCESS_ALLOWED;
}
break;
case INTERCEPTOR:
// If the object has an interceptor, try real named properties.
// Overwrite the result to fetch the correct property later.
holder->LookupRealNamedProperty(name, &lookup);
if (lookup.IsProperty() && lookup.IsPropertyCallbacks()) {
if (CheckAccessException(lookup.GetCallbackObject(), access_type)) {
return ACCESS_ALLOWED;
}
}
break;
default:
break;
}
isolate->ReportFailedAccessCheck(obj, access_type);
return ACCESS_FORBIDDEN;
}
// Enumerator used as indices into the array returned from GetOwnProperty
enum PropertyDescriptorIndices {
IS_ACCESSOR_INDEX,
VALUE_INDEX,
GETTER_INDEX,
SETTER_INDEX,
WRITABLE_INDEX,
ENUMERABLE_INDEX,
CONFIGURABLE_INDEX,
DESCRIPTOR_SIZE
};
MUST_USE_RESULT static MaybeHandle<Object> GetOwnProperty(Isolate* isolate,
Handle<JSObject> obj,
Handle<Name> name) {
Heap* heap = isolate->heap();
Factory* factory = isolate->factory();
// Due to some WebKit tests, we want to make sure that we do not log
// more than one access failure here.
AccessCheckResult access_check_result =
CheckPropertyAccess(obj, name, v8::ACCESS_HAS);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
switch (access_check_result) {
case ACCESS_FORBIDDEN: return factory->false_value();
case ACCESS_ALLOWED: break;
case ACCESS_ABSENT: return factory->undefined_value();
}
PropertyAttributes attrs = JSReceiver::GetOwnPropertyAttributes(obj, name);
if (attrs == ABSENT) {
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
return factory->undefined_value();
}
ASSERT(!isolate->has_scheduled_exception());
Handle<AccessorPair> accessors;
bool has_accessors =
JSObject::GetOwnPropertyAccessorPair(obj, name).ToHandle(&accessors);
Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE);
elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0));
elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0));
elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(has_accessors));
if (!has_accessors) {
elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0));
// Runtime::GetObjectProperty does access check.
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value, Runtime::GetObjectProperty(isolate, obj, name),
Object);
elms->set(VALUE_INDEX, *value);
} else {
// Access checks are performed for both accessors separately.
// When they fail, the respective field is not set in the descriptor.
Handle<Object> getter(accessors->GetComponent(ACCESSOR_GETTER), isolate);
Handle<Object> setter(accessors->GetComponent(ACCESSOR_SETTER), isolate);
if (!getter->IsMap() && CheckPropertyAccess(obj, name, v8::ACCESS_GET)) {
ASSERT(!isolate->has_scheduled_exception());
elms->set(GETTER_INDEX, *getter);
} else {
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
}
if (!setter->IsMap() && CheckPropertyAccess(obj, name, v8::ACCESS_SET)) {
ASSERT(!isolate->has_scheduled_exception());
elms->set(SETTER_INDEX, *setter);
} else {
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
}
}
return isolate->factory()->NewJSArrayWithElements(elms);
}
// Returns an array with the property description:
// if args[1] is not a property on args[0]
// returns undefined
// if args[1] is a data property on args[0]
// [false, value, Writeable, Enumerable, Configurable]
// if args[1] is an accessor on args[0]
// [true, GetFunction, SetFunction, Enumerable, Configurable]
RUNTIME_FUNCTION(Runtime_GetOwnProperty) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, GetOwnProperty(isolate, obj, name));
return *result;
}
RUNTIME_FUNCTION(Runtime_PreventExtensions) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, JSObject::PreventExtensions(obj));
return *result;
}
RUNTIME_FUNCTION(Runtime_IsExtensible) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
if (obj->IsJSGlobalProxy()) {
Object* proto = obj->GetPrototype();
if (proto->IsNull()) return isolate->heap()->false_value();
ASSERT(proto->IsJSGlobalObject());
obj = JSObject::cast(proto);
}
return isolate->heap()->ToBoolean(obj->map()->is_extensible());
}
RUNTIME_FUNCTION(Runtime_RegExpCompile) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSRegExp, re, 0);
CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
CONVERT_ARG_HANDLE_CHECKED(String, flags, 2);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, RegExpImpl::Compile(re, pattern, flags));
return *result;
}
RUNTIME_FUNCTION(Runtime_CreateApiFunction) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(FunctionTemplateInfo, data, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
return *isolate->factory()->CreateApiFunction(data, prototype);
}
RUNTIME_FUNCTION(Runtime_IsTemplate) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, arg, 0);
bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
return isolate->heap()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_GetTemplateField) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(HeapObject, templ, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
int offset = index * kPointerSize + HeapObject::kHeaderSize;
InstanceType type = templ->map()->instance_type();
RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
type == OBJECT_TEMPLATE_INFO_TYPE);
RUNTIME_ASSERT(offset > 0);
if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
} else {
RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
}
return *HeapObject::RawField(templ, offset);
}
RUNTIME_FUNCTION(Runtime_DisableAccessChecks) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(HeapObject, object, 0);
Handle<Map> old_map(object->map());
bool needs_access_checks = old_map->is_access_check_needed();
if (needs_access_checks) {
// Copy map so it won't interfere constructor's initial map.
Handle<Map> new_map = Map::Copy(old_map);
new_map->set_is_access_check_needed(false);
if (object->IsJSObject()) {
JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map);
} else {
object->set_map(*new_map);
}
}
return isolate->heap()->ToBoolean(needs_access_checks);
}
RUNTIME_FUNCTION(Runtime_EnableAccessChecks) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(HeapObject, object, 0);
Handle<Map> old_map(object->map());
if (!old_map->is_access_check_needed()) {
// Copy map so it won't interfere constructor's initial map.
Handle<Map> new_map = Map::Copy(old_map);
new_map->set_is_access_check_needed(true);
if (object->IsJSObject()) {
JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map);
} else {
object->set_map(*new_map);
}
}
return isolate->heap()->undefined_value();
}
// Transform getter or setter into something DefineAccessor can handle.
static Handle<Object> InstantiateAccessorComponent(Isolate* isolate,
Handle<Object> component) {
if (component->IsUndefined()) return isolate->factory()->null_value();
Handle<FunctionTemplateInfo> info =
Handle<FunctionTemplateInfo>::cast(component);
return Utils::OpenHandle(*Utils::ToLocal(info)->GetFunction());
}
RUNTIME_FUNCTION(Runtime_SetAccessorProperty) {
HandleScope scope(isolate);
ASSERT(args.length() == 6);
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, getter, 2);
CONVERT_ARG_HANDLE_CHECKED(Object, setter, 3);
CONVERT_SMI_ARG_CHECKED(attribute, 4);
CONVERT_SMI_ARG_CHECKED(access_control, 5);
RUNTIME_ASSERT(getter->IsUndefined() || getter->IsFunctionTemplateInfo());
RUNTIME_ASSERT(setter->IsUndefined() || setter->IsFunctionTemplateInfo());
RUNTIME_ASSERT(PropertyDetails::AttributesField::is_valid(
static_cast<PropertyAttributes>(attribute)));
JSObject::DefineAccessor(object,
name,
InstantiateAccessorComponent(isolate, getter),
InstantiateAccessorComponent(isolate, setter),
static_cast<PropertyAttributes>(attribute),
static_cast<v8::AccessControl>(access_control));
return isolate->heap()->undefined_value();
}
static Object* ThrowRedeclarationError(Isolate* isolate, Handle<String> name) {
HandleScope scope(isolate);
Handle<Object> args[1] = { name };
Handle<Object> error = isolate->factory()->NewTypeError(
"var_redeclaration", HandleVector(args, 1));
return isolate->Throw(*error);
}
RUNTIME_FUNCTION(RuntimeHidden_DeclareGlobals) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
Handle<GlobalObject> global = Handle<GlobalObject>(
isolate->context()->global_object());
CONVERT_ARG_HANDLE_CHECKED(Context, context, 0);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, pairs, 1);
CONVERT_SMI_ARG_CHECKED(flags, 2);
// Traverse the name/value pairs and set the properties.
int length = pairs->length();
for (int i = 0; i < length; i += 2) {
HandleScope scope(isolate);
Handle<String> name(String::cast(pairs->get(i)));
Handle<Object> value(pairs->get(i + 1), isolate);
// We have to declare a global const property. To capture we only
// assign to it when evaluating the assignment for "const x =
// <expr>" the initial value is the hole.
bool is_var = value->IsUndefined();
bool is_const = value->IsTheHole();
bool is_function = value->IsSharedFunctionInfo();
ASSERT(is_var + is_const + is_function == 1);
if (is_var || is_const) {
// Lookup the property in the global object, and don't set the
// value of the variable if the property is already there.
// Do the lookup own properties only, see ES5 erratum.
LookupResult lookup(isolate);
global->LookupOwn(name, &lookup, true);
if (lookup.IsFound()) {
// We found an existing property. Unless it was an interceptor
// that claims the property is absent, skip this declaration.
if (!lookup.IsInterceptor()) continue;
if (JSReceiver::GetPropertyAttributes(global, name) != ABSENT) continue;
// Fall-through and introduce the absent property by using
// SetProperty.
}
} else if (is_function) {
// Copy the function and update its context. Use it as value.
Handle<SharedFunctionInfo> shared =
Handle<SharedFunctionInfo>::cast(value);
Handle<JSFunction> function =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
shared, context, TENURED);
value = function;
}
LookupResult lookup(isolate);
global->LookupOwn(name, &lookup, true);
// Compute the property attributes. According to ECMA-262,
// the property must be non-configurable except in eval.
int attr = NONE;
bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
if (!is_eval) {
attr |= DONT_DELETE;
}
bool is_native = DeclareGlobalsNativeFlag::decode(flags);
if (is_const || (is_native && is_function)) {
attr |= READ_ONLY;
}
StrictMode strict_mode = DeclareGlobalsStrictMode::decode(flags);
if (!lookup.IsFound() || is_function) {
// If the own property exists, check that we can reconfigure it
// as required for function declarations.
if (lookup.IsFound() && lookup.IsDontDelete()) {
if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(isolate, name);
}
// If the existing property is not configurable, keep its attributes.
attr = lookup.GetAttributes();
}
// Define or redefine own property.
RETURN_FAILURE_ON_EXCEPTION(isolate,
JSObject::SetOwnPropertyIgnoreAttributes(
global, name, value, static_cast<PropertyAttributes>(attr)));
} else {
// Do a [[Put]] on the existing (own) property.
RETURN_FAILURE_ON_EXCEPTION(
isolate,
JSObject::SetProperty(
global, name, value, static_cast<PropertyAttributes>(attr),
strict_mode));
}
}
ASSERT(!isolate->has_pending_exception());
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(RuntimeHidden_DeclareContextSlot) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
// Declarations are always made in a function or native context. In the
// case of eval code, the context passed is the context of the caller,
// which may be some nested context and not the declaration context.
CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0);
Handle<Context> context(context_arg->declaration_context());
CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
CONVERT_SMI_ARG_CHECKED(mode_arg, 2);
PropertyAttributes mode = static_cast<PropertyAttributes>(mode_arg);
RUNTIME_ASSERT(mode == READ_ONLY || mode == NONE);
CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3);
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
BindingFlags binding_flags;
Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes, &binding_flags);
if (attributes != ABSENT) {
// The name was declared before; check for conflicting re-declarations.
// Note: this is actually inconsistent with what happens for globals (where
// we silently ignore such declarations).
if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
// Functions are not read-only.
ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
return ThrowRedeclarationError(isolate, name);
}
// Initialize it if necessary.
if (*initial_value != NULL) {
if (index >= 0) {
ASSERT(holder.is_identical_to(context));
if (((attributes & READ_ONLY) == 0) ||
context->get(index)->IsTheHole()) {
context->set(index, *initial_value);
}
} else {
// Slow case: The property is in the context extension object of a
// function context or the global object of a native context.
Handle<JSObject> object = Handle<JSObject>::cast(holder);
RETURN_FAILURE_ON_EXCEPTION(
isolate,
JSReceiver::SetProperty(object, name, initial_value, mode, SLOPPY));
}
}
} else {
// The property is not in the function context. It needs to be
// "declared" in the function context's extension context or as a
// property of the the global object.
Handle<JSObject> object;
if (context->has_extension()) {
object = Handle<JSObject>(JSObject::cast(context->extension()));
} else {
// Context extension objects are allocated lazily.
ASSERT(context->IsFunctionContext());
object = isolate->factory()->NewJSObject(
isolate->context_extension_function());
context->set_extension(*object);
}
ASSERT(*object != NULL);
// Declare the property by setting it to the initial value if provided,
// or undefined, and use the correct mode (e.g. READ_ONLY attribute for
// constant declarations).
ASSERT(!JSReceiver::HasOwnProperty(object, name));
Handle<Object> value(isolate->heap()->undefined_value(), isolate);
if (*initial_value != NULL) value = initial_value;
// Declaring a const context slot is a conflicting declaration if
// there is a callback with that name in a prototype. It is
// allowed to introduce const variables in
// JSContextExtensionObjects. They are treated specially in
// SetProperty and no setters are invoked for those since they are
// not real JSObjects.
if (initial_value->IsTheHole() &&
!object->IsJSContextExtensionObject()) {
LookupResult lookup(isolate);
object->Lookup(name, &lookup);
if (lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(isolate, name);
}
}
if (object->IsJSGlobalObject()) {
// Define own property on the global object.
RETURN_FAILURE_ON_EXCEPTION(isolate,
JSObject::SetOwnPropertyIgnoreAttributes(object, name, value, mode));
} else {
RETURN_FAILURE_ON_EXCEPTION(isolate,
JSReceiver::SetProperty(object, name, value, mode, SLOPPY));
}
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_InitializeVarGlobal) {
HandleScope scope(isolate);
// args[0] == name
// args[1] == language_mode
// args[2] == value (optional)
// Determine if we need to assign to the variable if it already
// exists (based on the number of arguments).
RUNTIME_ASSERT(args.length() == 2 || args.length() == 3);
bool assign = args.length() == 3;
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 1);
// According to ECMA-262, section 12.2, page 62, the property must
// not be deletable.
PropertyAttributes attributes = DONT_DELETE;
// Lookup the property as own on the global object. If it isn't
// there, there is a property with this name in the prototype chain.
// We follow Safari and Firefox behavior and only set the property
// if there is an explicit initialization value that we have
// to assign to the property.
// Note that objects can have hidden prototypes, so we need to traverse
// the whole chain of hidden prototypes to do an 'own' lookup.
LookupResult lookup(isolate);
isolate->context()->global_object()->LookupOwn(name, &lookup, true);
if (lookup.IsInterceptor()) {
Handle<JSObject> holder(lookup.holder());
PropertyAttributes intercepted =
JSReceiver::GetPropertyAttributes(holder, name);
if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
// Found an interceptor that's not read only.
if (assign) {
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
JSObject::SetPropertyForResult(
holder, &lookup, name, value, attributes, strict_mode));
return *result;
} else {
return isolate->heap()->undefined_value();
}
}
}
if (assign) {
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
Handle<GlobalObject> global(isolate->context()->global_object());
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
JSReceiver::SetProperty(global, name, value, attributes, strict_mode));
return *result;
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(RuntimeHidden_InitializeConstGlobal) {
SealHandleScope shs(isolate);
// All constants are declared with an initial value. The name
// of the constant is the first argument and the initial value
// is the second.
RUNTIME_ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
// Get the current global object from top.
GlobalObject* global = isolate->context()->global_object();
// According to ECMA-262, section 12.2, page 62, the property must
// not be deletable. Since it's a const, it must be READ_ONLY too.
PropertyAttributes attributes =
static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
// Lookup the property as own on the global object. If it isn't
// there, we add the property and take special precautions to always
// add it even in case of callbacks in the prototype chain (this rules