[refactor] Separate generated builtins and C++ builtins into separate files
This is in preparation for linking the former only into mksnapshot. Just shuffling code around, no changes in functionality. BUG=v8:6055 Review-Url: https://codereview.chromium.org/2752143004 Cr-Commit-Position: refs/heads/master@{#43858}
This commit is contained in:
parent
24b9ffa487
commit
b3507ff022
65
BUILD.gn
65
BUILD.gn
@ -808,6 +808,7 @@ v8_source_set("v8_nosnapshot") {
|
||||
":js2c_experimental_extras",
|
||||
":js2c_extras",
|
||||
":v8_base",
|
||||
":v8_builtins_generators",
|
||||
]
|
||||
|
||||
sources = [
|
||||
@ -833,6 +834,7 @@ v8_source_set("v8_snapshot") {
|
||||
":js2c_experimental_extras",
|
||||
":js2c_extras",
|
||||
":v8_base",
|
||||
":v8_builtins_generators",
|
||||
]
|
||||
public_deps = [
|
||||
# This should be public so downstream targets can declare the snapshot
|
||||
@ -859,6 +861,7 @@ if (v8_use_external_startup_data) {
|
||||
":js2c_experimental_extras",
|
||||
":js2c_extras",
|
||||
":v8_base",
|
||||
":v8_builtins_generators",
|
||||
]
|
||||
public_deps = [
|
||||
":natives_blob",
|
||||
@ -874,6 +877,53 @@ if (v8_use_external_startup_data) {
|
||||
}
|
||||
}
|
||||
|
||||
v8_source_set("v8_builtins_generators") {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
|
||||
deps = [
|
||||
":v8_base",
|
||||
]
|
||||
|
||||
sources = [
|
||||
### gcmole(all) ###
|
||||
"src/builtins/builtins-arguments-gen.cc",
|
||||
"src/builtins/builtins-arguments.h",
|
||||
"src/builtins/builtins-array-gen.cc",
|
||||
"src/builtins/builtins-async-function-gen.cc",
|
||||
"src/builtins/builtins-async-gen.cc",
|
||||
"src/builtins/builtins-async-iterator-gen.cc",
|
||||
"src/builtins/builtins-async.h",
|
||||
"src/builtins/builtins-boolean-gen.cc",
|
||||
"src/builtins/builtins-constructor-gen.cc",
|
||||
"src/builtins/builtins-constructor.h",
|
||||
"src/builtins/builtins-conversion-gen.cc",
|
||||
"src/builtins/builtins-date-gen.cc",
|
||||
"src/builtins/builtins-forin-gen.cc",
|
||||
"src/builtins/builtins-forin.h",
|
||||
"src/builtins/builtins-function-gen.cc",
|
||||
"src/builtins/builtins-generator-gen.cc",
|
||||
"src/builtins/builtins-global-gen.cc",
|
||||
"src/builtins/builtins-handler-gen.cc",
|
||||
"src/builtins/builtins-ic-gen.cc",
|
||||
"src/builtins/builtins-internal-gen.cc",
|
||||
"src/builtins/builtins-math-gen.cc",
|
||||
"src/builtins/builtins-number-gen.cc",
|
||||
"src/builtins/builtins-object-gen.cc",
|
||||
"src/builtins/builtins-promise-gen.cc",
|
||||
"src/builtins/builtins-promise.h",
|
||||
"src/builtins/builtins-regexp-gen.cc",
|
||||
"src/builtins/builtins-regexp-gen.h",
|
||||
"src/builtins/builtins-sharedarraybuffer-gen.cc",
|
||||
"src/builtins/builtins-string-gen.cc",
|
||||
"src/builtins/builtins-symbol-gen.cc",
|
||||
"src/builtins/builtins-typedarray-gen.cc",
|
||||
"src/builtins/builtins-utils-gen.h",
|
||||
"src/builtins/builtins-wasm-gen.cc",
|
||||
]
|
||||
|
||||
configs = [ ":internal_config" ]
|
||||
}
|
||||
|
||||
# This is split out to be a non-code containing target that the Chromium browser
|
||||
# DLL can depend upon to get only a version string.
|
||||
v8_header_set("v8_version") {
|
||||
@ -972,49 +1022,36 @@ v8_source_set("v8_base") {
|
||||
"src/bootstrapper.cc",
|
||||
"src/bootstrapper.h",
|
||||
"src/builtins/builtins-api.cc",
|
||||
"src/builtins/builtins-arguments.cc",
|
||||
"src/builtins/builtins-arguments.h",
|
||||
"src/builtins/builtins-array.cc",
|
||||
"src/builtins/builtins-arraybuffer.cc",
|
||||
"src/builtins/builtins-async-function.cc",
|
||||
"src/builtins/builtins-async-iterator.cc",
|
||||
"src/builtins/builtins-async.cc",
|
||||
"src/builtins/builtins-async.h",
|
||||
"src/builtins/builtins-boolean.cc",
|
||||
"src/builtins/builtins-call.cc",
|
||||
"src/builtins/builtins-callsite.cc",
|
||||
"src/builtins/builtins-constructor.cc",
|
||||
"src/builtins/builtins-constructor.h",
|
||||
"src/builtins/builtins-conversion.cc",
|
||||
"src/builtins/builtins-dataview.cc",
|
||||
"src/builtins/builtins-date.cc",
|
||||
"src/builtins/builtins-debug.cc",
|
||||
"src/builtins/builtins-error.cc",
|
||||
"src/builtins/builtins-forin.cc",
|
||||
"src/builtins/builtins-forin.h",
|
||||
"src/builtins/builtins-function.cc",
|
||||
"src/builtins/builtins-generator.cc",
|
||||
"src/builtins/builtins-global.cc",
|
||||
"src/builtins/builtins-handler.cc",
|
||||
"src/builtins/builtins-ic.cc",
|
||||
"src/builtins/builtins-internal.cc",
|
||||
"src/builtins/builtins-interpreter.cc",
|
||||
"src/builtins/builtins-json.cc",
|
||||
"src/builtins/builtins-math.cc",
|
||||
"src/builtins/builtins-number.cc",
|
||||
"src/builtins/builtins-object.cc",
|
||||
"src/builtins/builtins-promise.cc",
|
||||
"src/builtins/builtins-promise.h",
|
||||
"src/builtins/builtins-proxy.cc",
|
||||
"src/builtins/builtins-reflect.cc",
|
||||
"src/builtins/builtins-regexp-gen.h",
|
||||
"src/builtins/builtins-regexp.cc",
|
||||
"src/builtins/builtins-regexp.h",
|
||||
"src/builtins/builtins-sharedarraybuffer.cc",
|
||||
"src/builtins/builtins-string.cc",
|
||||
"src/builtins/builtins-symbol.cc",
|
||||
"src/builtins/builtins-typedarray.cc",
|
||||
"src/builtins/builtins-utils.h",
|
||||
"src/builtins/builtins-wasm.cc",
|
||||
"src/builtins/builtins.cc",
|
||||
"src/builtins/builtins.h",
|
||||
"src/cached-powers.cc",
|
||||
|
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-arguments.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
1518
src/builtins/builtins-array-gen.cc
Normal file
1518
src/builtins/builtins-array-gen.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-async.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/objects-inl.h"
|
||||
@ -11,13 +11,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
typedef compiler::Node Node;
|
||||
typedef CodeStubAssembler::ParameterMode ParameterMode;
|
||||
typedef compiler::CodeAssemblerState CodeAssemblerState;
|
||||
|
||||
class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
|
||||
public:
|
||||
explicit AsyncFunctionBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit AsyncFunctionBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: AsyncBuiltinsAssembler(state) {}
|
||||
|
||||
protected:
|
@ -3,15 +3,12 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-async.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/frames-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
|
||||
Node* AsyncBuiltinsAssembler::Await(
|
||||
Node* context, Node* generator, Node* value, Node* outer_promise,
|
||||
const NodeGenerator1& create_closure_context, int on_resolve_context_index,
|
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-async.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
@ -12,6 +12,8 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
|
||||
namespace {
|
||||
|
||||
// Describe fields of Context associated with the AsyncIterator unwrap closure.
|
||||
@ -22,7 +24,7 @@ class ValueUnwrapContext {
|
||||
|
||||
class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
|
||||
public:
|
||||
explicit AsyncFromSyncBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: AsyncBuiltinsAssembler(state) {}
|
||||
|
||||
void ThrowIfNotAsyncFromSyncIterator(Node* const context, Node* const object,
|
@ -12,7 +12,7 @@ namespace internal {
|
||||
|
||||
class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
public:
|
||||
explicit AsyncBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit AsyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: PromiseBuiltinsAssembler(state) {}
|
||||
|
||||
protected:
|
||||
|
37
src/builtins/builtins-boolean-gen.cc
Normal file
37
src/builtins/builtins-boolean-gen.cc
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 19.3 Boolean Objects
|
||||
|
||||
// ES6 section 19.3.3.2 Boolean.prototype.toString ( )
|
||||
TF_BUILTIN(BooleanPrototypeToString, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Node* value = ToThisValue(context, receiver, PrimitiveType::kBoolean,
|
||||
"Boolean.prototype.toString");
|
||||
Node* result = LoadObjectField(value, Oddball::kToStringOffset);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 19.3.3.3 Boolean.prototype.valueOf ( )
|
||||
TF_BUILTIN(BooleanPrototypeValueOf, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Node* result = ToThisValue(context, receiver, PrimitiveType::kBoolean,
|
||||
"Boolean.prototype.valueOf");
|
||||
Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
@ -36,34 +35,5 @@ BUILTIN(BooleanConstructor_ConstructStub) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
// ES6 section 19.3.3.2 Boolean.prototype.toString ( )
|
||||
void Builtins::Generate_BooleanPrototypeToString(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
typedef compiler::Node Node;
|
||||
CodeStubAssembler assembler(state);
|
||||
|
||||
Node* receiver = assembler.Parameter(0);
|
||||
Node* context = assembler.Parameter(3);
|
||||
|
||||
Node* value = assembler.ToThisValue(
|
||||
context, receiver, PrimitiveType::kBoolean, "Boolean.prototype.toString");
|
||||
Node* result = assembler.LoadObjectField(value, Oddball::kToStringOffset);
|
||||
assembler.Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 19.3.3.3 Boolean.prototype.valueOf ( )
|
||||
void Builtins::Generate_BooleanPrototypeValueOf(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
typedef compiler::Node Node;
|
||||
CodeStubAssembler assembler(state);
|
||||
|
||||
Node* receiver = assembler.Parameter(0);
|
||||
Node* context = assembler.Parameter(3);
|
||||
|
||||
Node* result = assembler.ToThisValue(
|
||||
context, receiver, PrimitiveType::kBoolean, "Boolean.prototype.valueOf");
|
||||
assembler.Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -2,9 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-constructor.h"
|
||||
#include "src/ast/ast.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-constructor.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
@ -21,9 +21,6 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
|
||||
Node* feedback_vector,
|
||||
Node* slot,
|
||||
Node* context) {
|
||||
typedef compiler::CodeAssembler::Label Label;
|
||||
typedef compiler::CodeAssembler::Variable Variable;
|
||||
|
||||
Isolate* isolate = this->isolate();
|
||||
Factory* factory = isolate->factory();
|
||||
IncrementCounter(isolate->counters()->fast_new_closure_total(), 1);
|
||||
@ -211,9 +208,10 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context,
|
||||
return var_obj.value();
|
||||
}
|
||||
|
||||
Node* ConstructorBuiltinsAssembler::EmitFastNewObject(
|
||||
Node* context, Node* target, Node* new_target,
|
||||
CodeAssemblerLabel* call_runtime) {
|
||||
Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context,
|
||||
Node* target,
|
||||
Node* new_target,
|
||||
Label* call_runtime) {
|
||||
CSA_ASSERT(this, HasInstanceType(target, JS_FUNCTION_TYPE));
|
||||
CSA_ASSERT(this, IsJSReceiver(new_target));
|
||||
|
||||
@ -403,18 +401,6 @@ TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) {
|
||||
ScopeType::FUNCTION_SCOPE));
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
|
||||
switch (scope_type) {
|
||||
case ScopeType::EVAL_SCOPE:
|
||||
return FastNewFunctionContextEval();
|
||||
case ScopeType::FUNCTION_SCOPE:
|
||||
return FastNewFunctionContextFunction();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Node* ConstructorBuiltinsAssembler::EmitFastCloneRegExp(Node* closure,
|
||||
Node* literal_index,
|
||||
Node* pattern,
|
||||
@ -501,12 +487,8 @@ Node* ConstructorBuiltinsAssembler::NonEmptyShallowClone(
|
||||
}
|
||||
|
||||
Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowArray(
|
||||
Node* closure, Node* literal_index, Node* context,
|
||||
CodeAssemblerLabel* call_runtime, AllocationSiteMode allocation_site_mode) {
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
typedef compiler::Node Node;
|
||||
|
||||
Node* closure, Node* literal_index, Node* context, Label* call_runtime,
|
||||
AllocationSiteMode allocation_site_mode) {
|
||||
Label zero_capacity(this), cow_elements(this), fast_elements(this),
|
||||
return_result(this);
|
||||
Variable result(this, MachineRepresentation::kTagged);
|
||||
@ -641,19 +623,6 @@ TF_BUILTIN(FastCloneShallowArrayDontTrack, ConstructorBuiltinsAssembler) {
|
||||
CreateFastCloneShallowArrayBuiltin(DONT_TRACK_ALLOCATION_SITE);
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::NewCloneShallowArray(
|
||||
AllocationSiteMode allocation_mode) {
|
||||
switch (allocation_mode) {
|
||||
case TRACK_ALLOCATION_SITE:
|
||||
return FastCloneShallowArrayTrack();
|
||||
case DONT_TRACK_ALLOCATION_SITE:
|
||||
return FastCloneShallowArrayDontTrack();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
// static
|
||||
int ConstructorBuiltinsAssembler::FastCloneShallowObjectPropertiesCount(
|
||||
int literal_length) {
|
||||
@ -667,7 +636,7 @@ int ConstructorBuiltinsAssembler::FastCloneShallowObjectPropertiesCount(
|
||||
}
|
||||
|
||||
Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
|
||||
CodeAssemblerLabel* call_runtime, Node* closure, Node* literals_index,
|
||||
Label* call_runtime, Node* closure, Node* literals_index,
|
||||
Node* properties_count) {
|
||||
Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset);
|
||||
Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset);
|
||||
@ -768,27 +737,5 @@ SHALLOW_OBJECT_BUILTIN(4);
|
||||
SHALLOW_OBJECT_BUILTIN(5);
|
||||
SHALLOW_OBJECT_BUILTIN(6);
|
||||
|
||||
Handle<Code> Builtins::NewCloneShallowObject(int length) {
|
||||
switch (length) {
|
||||
case 0:
|
||||
return FastCloneShallowObject0();
|
||||
case 1:
|
||||
return FastCloneShallowObject1();
|
||||
case 2:
|
||||
return FastCloneShallowObject2();
|
||||
case 3:
|
||||
return FastCloneShallowObject3();
|
||||
case 4:
|
||||
return FastCloneShallowObject4();
|
||||
case 5:
|
||||
return FastCloneShallowObject5();
|
||||
case 6:
|
||||
return FastCloneShallowObject6();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -7,13 +7,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
typedef compiler::Node Node;
|
||||
typedef compiler::CodeAssemblerState CodeAssemblerState;
|
||||
typedef compiler::CodeAssemblerLabel CodeAssemblerLabel;
|
||||
|
||||
class ConstructorBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit ConstructorBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit ConstructorBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
Node* EmitFastNewClosure(Node* shared_info, Node* feedback_vector, Node* slot,
|
||||
@ -25,8 +21,7 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
|
||||
Node* EmitFastCloneRegExp(Node* closure, Node* literal_index, Node* pattern,
|
||||
Node* flags, Node* context);
|
||||
Node* EmitFastCloneShallowArray(Node* closure, Node* literal_index,
|
||||
Node* context,
|
||||
CodeAssemblerLabel* call_runtime,
|
||||
Node* context, Label* call_runtime,
|
||||
AllocationSiteMode allocation_site_mode);
|
||||
|
||||
// Maximum number of elements in copied array (chosen so that even an array
|
||||
@ -40,15 +35,15 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
|
||||
// Maximum number of properties in copied objects.
|
||||
static const int kMaximumClonedShallowObjectProperties = 6;
|
||||
static int FastCloneShallowObjectPropertiesCount(int literal_length);
|
||||
Node* EmitFastCloneShallowObject(CodeAssemblerLabel* call_runtime,
|
||||
Node* closure, Node* literals_index,
|
||||
Node* EmitFastCloneShallowObject(Label* call_runtime, Node* closure,
|
||||
Node* literals_index,
|
||||
Node* properties_count);
|
||||
void CreateFastCloneShallowObjectBuiltin(int properties_count);
|
||||
|
||||
Node* EmitFastNewObject(Node* context, Node* target, Node* new_target);
|
||||
|
||||
Node* EmitFastNewObject(Node* context, Node* target, Node* new_target,
|
||||
CodeAssemblerLabel* call_runtime);
|
||||
Label* call_runtime);
|
||||
|
||||
private:
|
||||
static const int kMaximumSlots = 0x8000;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
@ -22,19 +22,6 @@ class ConversionBuiltinsAssembler : public CodeStubAssembler {
|
||||
void Generate_OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint);
|
||||
};
|
||||
|
||||
Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
|
||||
switch (hint) {
|
||||
case ToPrimitiveHint::kDefault:
|
||||
return NonPrimitiveToPrimitive_Default();
|
||||
case ToPrimitiveHint::kNumber:
|
||||
return NonPrimitiveToPrimitive_Number();
|
||||
case ToPrimitiveHint::kString:
|
||||
return NonPrimitiveToPrimitive_String();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
|
||||
void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
|
||||
ToPrimitiveHint hint) {
|
||||
@ -169,17 +156,6 @@ TF_BUILTIN(ToString, CodeStubAssembler) {
|
||||
{ Return(CallRuntime(Runtime::kToString, context, input)); }
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
|
||||
switch (hint) {
|
||||
case OrdinaryToPrimitiveHint::kNumber:
|
||||
return OrdinaryToPrimitive_Number();
|
||||
case OrdinaryToPrimitiveHint::kString:
|
||||
return OrdinaryToPrimitive_String();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
|
||||
void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
|
||||
OrdinaryToPrimitiveHint hint) {
|
220
src/builtins/builtins-date-gen.cc
Normal file
220
src/builtins/builtins-date-gen.cc
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 20.3 Date Objects
|
||||
|
||||
class DateBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit DateBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void Generate_DatePrototype_GetField(int field_index);
|
||||
};
|
||||
|
||||
void DateBuiltinsAssembler::Generate_DatePrototype_GetField(int field_index) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Label receiver_not_date(this, Label::kDeferred);
|
||||
|
||||
GotoIf(TaggedIsSmi(receiver), &receiver_not_date);
|
||||
Node* receiver_instance_type = LoadInstanceType(receiver);
|
||||
GotoIf(Word32NotEqual(receiver_instance_type, Int32Constant(JS_DATE_TYPE)),
|
||||
&receiver_not_date);
|
||||
|
||||
// Load the specified date field, falling back to the runtime as necessary.
|
||||
if (field_index == JSDate::kDateValue) {
|
||||
Return(LoadObjectField(receiver, JSDate::kValueOffset));
|
||||
} else {
|
||||
if (field_index < JSDate::kFirstUncachedField) {
|
||||
Label stamp_mismatch(this, Label::kDeferred);
|
||||
Node* date_cache_stamp = Load(
|
||||
MachineType::AnyTagged(),
|
||||
ExternalConstant(ExternalReference::date_cache_stamp(isolate())));
|
||||
|
||||
Node* cache_stamp = LoadObjectField(receiver, JSDate::kCacheStampOffset);
|
||||
GotoIf(WordNotEqual(date_cache_stamp, cache_stamp), &stamp_mismatch);
|
||||
Return(LoadObjectField(
|
||||
receiver, JSDate::kValueOffset + field_index * kPointerSize));
|
||||
|
||||
Bind(&stamp_mismatch);
|
||||
}
|
||||
|
||||
Node* field_index_smi = SmiConstant(Smi::FromInt(field_index));
|
||||
Node* function =
|
||||
ExternalConstant(ExternalReference::get_date_field_function(isolate()));
|
||||
Node* result = CallCFunction2(
|
||||
MachineType::AnyTagged(), MachineType::AnyTagged(),
|
||||
MachineType::AnyTagged(), function, receiver, field_index_smi);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// Raise a TypeError if the receiver is not a date.
|
||||
Bind(&receiver_not_date);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowNotDateError, context);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetDate, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDay);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetDay, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kWeekday);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetFullYear, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kYear);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetHours, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kHour);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetMilliseconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMillisecond);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetMinutes, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMinute);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetMonth, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMonth);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetSeconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kSecond);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetTime, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDateValue);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetTimezoneOffset, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kTimezoneOffset);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCDate, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDayUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCDay, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kWeekdayUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCFullYear, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kYearUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCHours, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kHourUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCMilliseconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMillisecondUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCMinutes, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMinuteUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCMonth, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMonthUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCSeconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kSecondUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeValueOf, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDateValue);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeToPrimitive, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* hint = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// Check if the {receiver} is actually a JSReceiver.
|
||||
Label receiver_is_invalid(this, Label::kDeferred);
|
||||
GotoIf(TaggedIsSmi(receiver), &receiver_is_invalid);
|
||||
GotoIfNot(IsJSReceiver(receiver), &receiver_is_invalid);
|
||||
|
||||
// Dispatch to the appropriate OrdinaryToPrimitive builtin.
|
||||
Label hint_is_number(this), hint_is_string(this),
|
||||
hint_is_invalid(this, Label::kDeferred);
|
||||
|
||||
// Fast cases for internalized strings.
|
||||
Node* number_string = LoadRoot(Heap::knumber_stringRootIndex);
|
||||
GotoIf(WordEqual(hint, number_string), &hint_is_number);
|
||||
Node* default_string = LoadRoot(Heap::kdefault_stringRootIndex);
|
||||
GotoIf(WordEqual(hint, default_string), &hint_is_string);
|
||||
Node* string_string = LoadRoot(Heap::kstring_stringRootIndex);
|
||||
GotoIf(WordEqual(hint, string_string), &hint_is_string);
|
||||
|
||||
// Slow-case with actual string comparisons.
|
||||
Callable string_equal = CodeFactory::StringEqual(isolate());
|
||||
GotoIf(TaggedIsSmi(hint), &hint_is_invalid);
|
||||
GotoIfNot(IsString(hint), &hint_is_invalid);
|
||||
GotoIf(WordEqual(CallStub(string_equal, context, hint, number_string),
|
||||
TrueConstant()),
|
||||
&hint_is_number);
|
||||
GotoIf(WordEqual(CallStub(string_equal, context, hint, default_string),
|
||||
TrueConstant()),
|
||||
&hint_is_string);
|
||||
GotoIf(WordEqual(CallStub(string_equal, context, hint, string_string),
|
||||
TrueConstant()),
|
||||
&hint_is_string);
|
||||
Goto(&hint_is_invalid);
|
||||
|
||||
// Use the OrdinaryToPrimitive builtin to convert to a Number.
|
||||
Bind(&hint_is_number);
|
||||
{
|
||||
Callable callable = CodeFactory::OrdinaryToPrimitive(
|
||||
isolate(), OrdinaryToPrimitiveHint::kNumber);
|
||||
Node* result = CallStub(callable, context, receiver);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// Use the OrdinaryToPrimitive builtin to convert to a String.
|
||||
Bind(&hint_is_string);
|
||||
{
|
||||
Callable callable = CodeFactory::OrdinaryToPrimitive(
|
||||
isolate(), OrdinaryToPrimitiveHint::kString);
|
||||
Node* result = CallStub(callable, context, receiver);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// Raise a TypeError if the {hint} is invalid.
|
||||
Bind(&hint_is_invalid);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowInvalidHint, context, hint);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
// Raise a TypeError if the {receiver} is not a JSReceiver instance.
|
||||
Bind(&receiver_is_invalid);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
|
||||
HeapConstant(factory()->NewStringFromAsciiChecked(
|
||||
"Date.prototype [ @@toPrimitive ]", TENURED)),
|
||||
receiver);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -17,15 +17,6 @@ namespace internal {
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 20.3 Date Objects
|
||||
|
||||
class DateBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit DateBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void Generate_DatePrototype_GetField(int field_index);
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
// ES6 section 20.3.1.1 Time Values and Time Range
|
||||
@ -904,200 +895,5 @@ BUILTIN(DatePrototypeToJson) {
|
||||
}
|
||||
}
|
||||
|
||||
void DateBuiltinsAssembler::Generate_DatePrototype_GetField(int field_index) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Label receiver_not_date(this, Label::kDeferred);
|
||||
|
||||
GotoIf(TaggedIsSmi(receiver), &receiver_not_date);
|
||||
Node* receiver_instance_type = LoadInstanceType(receiver);
|
||||
GotoIf(Word32NotEqual(receiver_instance_type, Int32Constant(JS_DATE_TYPE)),
|
||||
&receiver_not_date);
|
||||
|
||||
// Load the specified date field, falling back to the runtime as necessary.
|
||||
if (field_index == JSDate::kDateValue) {
|
||||
Return(LoadObjectField(receiver, JSDate::kValueOffset));
|
||||
} else {
|
||||
if (field_index < JSDate::kFirstUncachedField) {
|
||||
Label stamp_mismatch(this, Label::kDeferred);
|
||||
Node* date_cache_stamp = Load(
|
||||
MachineType::AnyTagged(),
|
||||
ExternalConstant(ExternalReference::date_cache_stamp(isolate())));
|
||||
|
||||
Node* cache_stamp = LoadObjectField(receiver, JSDate::kCacheStampOffset);
|
||||
GotoIf(WordNotEqual(date_cache_stamp, cache_stamp), &stamp_mismatch);
|
||||
Return(LoadObjectField(
|
||||
receiver, JSDate::kValueOffset + field_index * kPointerSize));
|
||||
|
||||
Bind(&stamp_mismatch);
|
||||
}
|
||||
|
||||
Node* field_index_smi = SmiConstant(Smi::FromInt(field_index));
|
||||
Node* function =
|
||||
ExternalConstant(ExternalReference::get_date_field_function(isolate()));
|
||||
Node* result = CallCFunction2(
|
||||
MachineType::AnyTagged(), MachineType::AnyTagged(),
|
||||
MachineType::AnyTagged(), function, receiver, field_index_smi);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// Raise a TypeError if the receiver is not a date.
|
||||
Bind(&receiver_not_date);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowNotDateError, context);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetDate, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDay);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetDay, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kWeekday);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetFullYear, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kYear);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetHours, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kHour);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetMilliseconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMillisecond);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetMinutes, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMinute);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetMonth, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMonth);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetSeconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kSecond);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetTime, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDateValue);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetTimezoneOffset, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kTimezoneOffset);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCDate, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDayUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCDay, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kWeekdayUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCFullYear, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kYearUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCHours, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kHourUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCMilliseconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMillisecondUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCMinutes, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMinuteUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCMonth, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kMonthUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeGetUTCSeconds, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kSecondUTC);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeValueOf, DateBuiltinsAssembler) {
|
||||
Generate_DatePrototype_GetField(JSDate::kDateValue);
|
||||
}
|
||||
|
||||
TF_BUILTIN(DatePrototypeToPrimitive, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* hint = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// Check if the {receiver} is actually a JSReceiver.
|
||||
Label receiver_is_invalid(this, Label::kDeferred);
|
||||
GotoIf(TaggedIsSmi(receiver), &receiver_is_invalid);
|
||||
GotoIfNot(IsJSReceiver(receiver), &receiver_is_invalid);
|
||||
|
||||
// Dispatch to the appropriate OrdinaryToPrimitive builtin.
|
||||
Label hint_is_number(this), hint_is_string(this),
|
||||
hint_is_invalid(this, Label::kDeferred);
|
||||
|
||||
// Fast cases for internalized strings.
|
||||
Node* number_string = LoadRoot(Heap::knumber_stringRootIndex);
|
||||
GotoIf(WordEqual(hint, number_string), &hint_is_number);
|
||||
Node* default_string = LoadRoot(Heap::kdefault_stringRootIndex);
|
||||
GotoIf(WordEqual(hint, default_string), &hint_is_string);
|
||||
Node* string_string = LoadRoot(Heap::kstring_stringRootIndex);
|
||||
GotoIf(WordEqual(hint, string_string), &hint_is_string);
|
||||
|
||||
// Slow-case with actual string comparisons.
|
||||
Callable string_equal = CodeFactory::StringEqual(isolate());
|
||||
GotoIf(TaggedIsSmi(hint), &hint_is_invalid);
|
||||
GotoIfNot(IsString(hint), &hint_is_invalid);
|
||||
GotoIf(WordEqual(CallStub(string_equal, context, hint, number_string),
|
||||
TrueConstant()),
|
||||
&hint_is_number);
|
||||
GotoIf(WordEqual(CallStub(string_equal, context, hint, default_string),
|
||||
TrueConstant()),
|
||||
&hint_is_string);
|
||||
GotoIf(WordEqual(CallStub(string_equal, context, hint, string_string),
|
||||
TrueConstant()),
|
||||
&hint_is_string);
|
||||
Goto(&hint_is_invalid);
|
||||
|
||||
// Use the OrdinaryToPrimitive builtin to convert to a Number.
|
||||
Bind(&hint_is_number);
|
||||
{
|
||||
Callable callable = CodeFactory::OrdinaryToPrimitive(
|
||||
isolate(), OrdinaryToPrimitiveHint::kNumber);
|
||||
Node* result = CallStub(callable, context, receiver);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// Use the OrdinaryToPrimitive builtin to convert to a String.
|
||||
Bind(&hint_is_string);
|
||||
{
|
||||
Callable callable = CodeFactory::OrdinaryToPrimitive(
|
||||
isolate(), OrdinaryToPrimitiveHint::kString);
|
||||
Node* result = CallStub(callable, context, receiver);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// Raise a TypeError if the {hint} is invalid.
|
||||
Bind(&hint_is_invalid);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowInvalidHint, context, hint);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
// Raise a TypeError if the {receiver} is not a JSReceiver instance.
|
||||
Bind(&receiver_is_invalid);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
|
||||
HeapConstant(factory()->NewStringFromAsciiChecked(
|
||||
"Date.prototype [ @@toPrimitive ]", TENURED)),
|
||||
receiver);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include "src/builtins/builtins-forin.h"
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
178
src/builtins/builtins-function-gen.cc
Normal file
178
src/builtins/builtins-function-gen.cc
Normal file
@ -0,0 +1,178 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
|
||||
Label slow(this);
|
||||
|
||||
Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
|
||||
Node* context = Parameter(BuiltinDescriptor::kContext);
|
||||
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
|
||||
|
||||
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
|
||||
|
||||
// Check that receiver has instance type of JS_FUNCTION_TYPE
|
||||
Node* receiver = args.GetReceiver();
|
||||
GotoIf(TaggedIsSmi(receiver), &slow);
|
||||
|
||||
Node* receiver_map = LoadMap(receiver);
|
||||
Node* instance_type = LoadMapInstanceType(receiver_map);
|
||||
GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)), &slow);
|
||||
|
||||
// Disallow binding of slow-mode functions. We need to figure out whether the
|
||||
// length and name property are in the original state.
|
||||
Comment("Disallow binding of slow-mode functions");
|
||||
GotoIf(IsDictionaryMap(receiver_map), &slow);
|
||||
|
||||
// Check whether the length and name properties are still present as
|
||||
// AccessorInfo objects. In that case, their value can be recomputed even if
|
||||
// the actual value on the object changes.
|
||||
Comment("Check descriptor array length");
|
||||
Node* descriptors = LoadMapDescriptors(receiver_map);
|
||||
Node* descriptors_length = LoadFixedArrayBaseLength(descriptors);
|
||||
GotoIf(SmiLessThanOrEqual(descriptors_length, SmiConstant(1)), &slow);
|
||||
|
||||
// Check whether the length and name properties are still present as
|
||||
// AccessorInfo objects. In that case, their value can be recomputed even if
|
||||
// the actual value on the object changes.
|
||||
Comment("Check name and length properties");
|
||||
const int length_index = JSFunction::kLengthDescriptorIndex;
|
||||
Node* maybe_length = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToKeyIndex(length_index));
|
||||
GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)),
|
||||
&slow);
|
||||
|
||||
Node* maybe_length_accessor = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToValueIndex(length_index));
|
||||
GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
|
||||
Node* length_value_map = LoadMap(maybe_length_accessor);
|
||||
GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
|
||||
|
||||
const int name_index = JSFunction::kNameDescriptorIndex;
|
||||
Node* maybe_name = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToKeyIndex(name_index));
|
||||
GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)),
|
||||
&slow);
|
||||
|
||||
Node* maybe_name_accessor = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToValueIndex(name_index));
|
||||
GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
|
||||
Node* name_value_map = LoadMap(maybe_name_accessor);
|
||||
GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
|
||||
|
||||
// Choose the right bound function map based on whether the target is
|
||||
// constructable.
|
||||
Comment("Choose the right bound function map");
|
||||
Variable bound_function_map(this, MachineRepresentation::kTagged);
|
||||
Label with_constructor(this);
|
||||
VariableList vars({&bound_function_map}, zone());
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
|
||||
Label map_done(this, vars);
|
||||
Node* bit_field = LoadMapBitField(receiver_map);
|
||||
int mask = static_cast<int>(1 << Map::kIsConstructor);
|
||||
GotoIf(IsSetWord32(bit_field, mask), &with_constructor);
|
||||
|
||||
bound_function_map.Bind(LoadContextElement(
|
||||
native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
|
||||
Goto(&map_done);
|
||||
|
||||
Bind(&with_constructor);
|
||||
bound_function_map.Bind(LoadContextElement(
|
||||
native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
|
||||
Goto(&map_done);
|
||||
|
||||
Bind(&map_done);
|
||||
|
||||
// Verify that __proto__ matches that of a the target bound function.
|
||||
Comment("Verify that __proto__ matches target bound function");
|
||||
Node* prototype = LoadMapPrototype(receiver_map);
|
||||
Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
|
||||
GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
|
||||
|
||||
// Allocate the arguments array.
|
||||
Comment("Allocate the arguments array");
|
||||
Variable argument_array(this, MachineRepresentation::kTagged);
|
||||
Label empty_arguments(this);
|
||||
Label arguments_done(this, &argument_array);
|
||||
GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
|
||||
Node* elements_length = ChangeUint32ToWord(Int32Sub(argc, Int32Constant(1)));
|
||||
Node* elements = AllocateFixedArray(FAST_ELEMENTS, elements_length);
|
||||
Variable index(this, MachineType::PointerRepresentation());
|
||||
index.Bind(IntPtrConstant(0));
|
||||
VariableList foreach_vars({&index}, zone());
|
||||
args.ForEach(foreach_vars,
|
||||
[this, elements, &index](Node* arg) {
|
||||
StoreFixedArrayElement(elements, index.value(), arg);
|
||||
Increment(index);
|
||||
},
|
||||
IntPtrConstant(1));
|
||||
argument_array.Bind(elements);
|
||||
Goto(&arguments_done);
|
||||
|
||||
Bind(&empty_arguments);
|
||||
argument_array.Bind(EmptyFixedArrayConstant());
|
||||
Goto(&arguments_done);
|
||||
|
||||
Bind(&arguments_done);
|
||||
|
||||
// Determine bound receiver.
|
||||
Comment("Determine bound receiver");
|
||||
Variable bound_receiver(this, MachineRepresentation::kTagged);
|
||||
Label has_receiver(this);
|
||||
Label receiver_done(this, &bound_receiver);
|
||||
GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
|
||||
bound_receiver.Bind(UndefinedConstant());
|
||||
Goto(&receiver_done);
|
||||
|
||||
Bind(&has_receiver);
|
||||
bound_receiver.Bind(args.AtIndex(0));
|
||||
Goto(&receiver_done);
|
||||
|
||||
Bind(&receiver_done);
|
||||
|
||||
// Allocate the resulting bound function.
|
||||
Comment("Allocate the resulting bound function");
|
||||
Node* bound_function = Allocate(JSBoundFunction::kSize);
|
||||
StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
|
||||
StoreObjectFieldNoWriteBarrier(bound_function,
|
||||
JSBoundFunction::kBoundThisOffset,
|
||||
bound_receiver.value());
|
||||
StoreObjectFieldNoWriteBarrier(bound_function,
|
||||
JSBoundFunction::kBoundArgumentsOffset,
|
||||
argument_array.value());
|
||||
Node* empty_fixed_array = EmptyFixedArrayConstant();
|
||||
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kPropertiesOffset,
|
||||
empty_fixed_array);
|
||||
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
|
||||
empty_fixed_array);
|
||||
|
||||
args.PopAndReturn(bound_function);
|
||||
Bind(&slow);
|
||||
|
||||
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
|
||||
MachineType::TaggedPointer());
|
||||
TailCallStub(CodeFactory::FunctionPrototypeBind(isolate()), context, target,
|
||||
new_target, argc);
|
||||
}
|
||||
|
||||
// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V )
|
||||
TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
|
||||
Node* f = Parameter(0);
|
||||
Node* v = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* result = OrdinaryHasInstance(context, f, v);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -5,7 +5,6 @@
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/compiler.h"
|
||||
#include "src/conversions.h"
|
||||
#include "src/counters.h"
|
||||
@ -272,162 +271,6 @@ Object* DoFunctionBind(Isolate* isolate, BuiltinArguments args) {
|
||||
// ES6 section 19.2.3.2 Function.prototype.bind ( thisArg, ...args )
|
||||
BUILTIN(FunctionPrototypeBind) { return DoFunctionBind(isolate, args); }
|
||||
|
||||
TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
|
||||
Label slow(this);
|
||||
|
||||
Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
|
||||
Node* context = Parameter(BuiltinDescriptor::kContext);
|
||||
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
|
||||
|
||||
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
|
||||
|
||||
// Check that receiver has instance type of JS_FUNCTION_TYPE
|
||||
Node* receiver = args.GetReceiver();
|
||||
GotoIf(TaggedIsSmi(receiver), &slow);
|
||||
|
||||
Node* receiver_map = LoadMap(receiver);
|
||||
Node* instance_type = LoadMapInstanceType(receiver_map);
|
||||
GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)), &slow);
|
||||
|
||||
// Disallow binding of slow-mode functions. We need to figure out whether the
|
||||
// length and name property are in the original state.
|
||||
Comment("Disallow binding of slow-mode functions");
|
||||
GotoIf(IsDictionaryMap(receiver_map), &slow);
|
||||
|
||||
// Check whether the length and name properties are still present as
|
||||
// AccessorInfo objects. In that case, their value can be recomputed even if
|
||||
// the actual value on the object changes.
|
||||
Comment("Check descriptor array length");
|
||||
Node* descriptors = LoadMapDescriptors(receiver_map);
|
||||
Node* descriptors_length = LoadFixedArrayBaseLength(descriptors);
|
||||
GotoIf(SmiLessThanOrEqual(descriptors_length, SmiConstant(1)), &slow);
|
||||
|
||||
// Check whether the length and name properties are still present as
|
||||
// AccessorInfo objects. In that case, their value can be recomputed even if
|
||||
// the actual value on the object changes.
|
||||
Comment("Check name and length properties");
|
||||
const int length_index = JSFunction::kLengthDescriptorIndex;
|
||||
Node* maybe_length = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToKeyIndex(length_index));
|
||||
GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)),
|
||||
&slow);
|
||||
|
||||
Node* maybe_length_accessor = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToValueIndex(length_index));
|
||||
GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
|
||||
Node* length_value_map = LoadMap(maybe_length_accessor);
|
||||
GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
|
||||
|
||||
const int name_index = JSFunction::kNameDescriptorIndex;
|
||||
Node* maybe_name = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToKeyIndex(name_index));
|
||||
GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)),
|
||||
&slow);
|
||||
|
||||
Node* maybe_name_accessor = LoadFixedArrayElement(
|
||||
descriptors, DescriptorArray::ToValueIndex(name_index));
|
||||
GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
|
||||
Node* name_value_map = LoadMap(maybe_name_accessor);
|
||||
GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
|
||||
|
||||
// Choose the right bound function map based on whether the target is
|
||||
// constructable.
|
||||
Comment("Choose the right bound function map");
|
||||
Variable bound_function_map(this, MachineRepresentation::kTagged);
|
||||
Label with_constructor(this);
|
||||
VariableList vars({&bound_function_map}, zone());
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
|
||||
Label map_done(this, vars);
|
||||
Node* bit_field = LoadMapBitField(receiver_map);
|
||||
int mask = static_cast<int>(1 << Map::kIsConstructor);
|
||||
GotoIf(IsSetWord32(bit_field, mask), &with_constructor);
|
||||
|
||||
bound_function_map.Bind(LoadContextElement(
|
||||
native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
|
||||
Goto(&map_done);
|
||||
|
||||
Bind(&with_constructor);
|
||||
bound_function_map.Bind(LoadContextElement(
|
||||
native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
|
||||
Goto(&map_done);
|
||||
|
||||
Bind(&map_done);
|
||||
|
||||
// Verify that __proto__ matches that of a the target bound function.
|
||||
Comment("Verify that __proto__ matches target bound function");
|
||||
Node* prototype = LoadMapPrototype(receiver_map);
|
||||
Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
|
||||
GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
|
||||
|
||||
// Allocate the arguments array.
|
||||
Comment("Allocate the arguments array");
|
||||
Variable argument_array(this, MachineRepresentation::kTagged);
|
||||
Label empty_arguments(this);
|
||||
Label arguments_done(this, &argument_array);
|
||||
GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
|
||||
Node* elements_length = ChangeUint32ToWord(Int32Sub(argc, Int32Constant(1)));
|
||||
Node* elements = AllocateFixedArray(FAST_ELEMENTS, elements_length);
|
||||
Variable index(this, MachineType::PointerRepresentation());
|
||||
index.Bind(IntPtrConstant(0));
|
||||
VariableList foreach_vars({&index}, zone());
|
||||
args.ForEach(foreach_vars,
|
||||
[this, elements, &index](Node* arg) {
|
||||
StoreFixedArrayElement(elements, index.value(), arg);
|
||||
Increment(index);
|
||||
},
|
||||
IntPtrConstant(1));
|
||||
argument_array.Bind(elements);
|
||||
Goto(&arguments_done);
|
||||
|
||||
Bind(&empty_arguments);
|
||||
argument_array.Bind(EmptyFixedArrayConstant());
|
||||
Goto(&arguments_done);
|
||||
|
||||
Bind(&arguments_done);
|
||||
|
||||
// Determine bound receiver.
|
||||
Comment("Determine bound receiver");
|
||||
Variable bound_receiver(this, MachineRepresentation::kTagged);
|
||||
Label has_receiver(this);
|
||||
Label receiver_done(this, &bound_receiver);
|
||||
GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
|
||||
bound_receiver.Bind(UndefinedConstant());
|
||||
Goto(&receiver_done);
|
||||
|
||||
Bind(&has_receiver);
|
||||
bound_receiver.Bind(args.AtIndex(0));
|
||||
Goto(&receiver_done);
|
||||
|
||||
Bind(&receiver_done);
|
||||
|
||||
// Allocate the resulting bound function.
|
||||
Comment("Allocate the resulting bound function");
|
||||
Node* bound_function = Allocate(JSBoundFunction::kSize);
|
||||
StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
|
||||
StoreObjectFieldNoWriteBarrier(bound_function,
|
||||
JSBoundFunction::kBoundThisOffset,
|
||||
bound_receiver.value());
|
||||
StoreObjectFieldNoWriteBarrier(bound_function,
|
||||
JSBoundFunction::kBoundArgumentsOffset,
|
||||
argument_array.value());
|
||||
Node* empty_fixed_array = EmptyFixedArrayConstant();
|
||||
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kPropertiesOffset,
|
||||
empty_fixed_array);
|
||||
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
|
||||
empty_fixed_array);
|
||||
|
||||
args.PopAndReturn(bound_function);
|
||||
Bind(&slow);
|
||||
|
||||
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
|
||||
MachineType::TaggedPointer());
|
||||
TailCallStub(CodeFactory::FunctionPrototypeBind(isolate()), context, target,
|
||||
new_target, argc);
|
||||
}
|
||||
|
||||
// TODO(verwaest): This is a temporary helper until the FastFunctionBind stub
|
||||
// can tailcall to the builtin directly.
|
||||
RUNTIME_FUNCTION(Runtime_FunctionBind) {
|
||||
@ -454,14 +297,5 @@ BUILTIN(FunctionPrototypeToString) {
|
||||
"Function.prototype.toString")));
|
||||
}
|
||||
|
||||
// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V )
|
||||
TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
|
||||
Node* f = Parameter(0);
|
||||
Node* v = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* result = OrdinaryHasInstance(context, f, v);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
@ -12,11 +12,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
typedef compiler::CodeAssemblerState CodeAssemblerState;
|
||||
|
||||
class GeneratorBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit GeneratorBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit GeneratorBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
108
src/builtins/builtins-global-gen.cc
Normal file
108
src/builtins/builtins-global-gen.cc
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// ES6 section 18.2.2 isFinite ( number )
|
||||
TF_BUILTIN(GlobalIsFinite, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Label return_true(this), return_false(this);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_num(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_num);
|
||||
var_num.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
Node* num = var_num.value();
|
||||
|
||||
// Check if {num} is a Smi or a HeapObject.
|
||||
GotoIf(TaggedIsSmi(num), &return_true);
|
||||
|
||||
// Check if {num} is a HeapNumber.
|
||||
Label if_numisheapnumber(this),
|
||||
if_numisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(num)), &if_numisheapnumber,
|
||||
&if_numisnotheapnumber);
|
||||
|
||||
Bind(&if_numisheapnumber);
|
||||
{
|
||||
// Check if {num} contains a finite, non-NaN value.
|
||||
Node* num_value = LoadHeapNumberValue(num);
|
||||
BranchIfFloat64IsNaN(Float64Sub(num_value, num_value), &return_false,
|
||||
&return_true);
|
||||
}
|
||||
|
||||
Bind(&if_numisnotheapnumber);
|
||||
{
|
||||
// Need to convert {num} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_num.Bind(CallStub(callable, context, num));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&return_true);
|
||||
Return(BooleanConstant(true));
|
||||
|
||||
Bind(&return_false);
|
||||
Return(BooleanConstant(false));
|
||||
}
|
||||
|
||||
// ES6 section 18.2.3 isNaN ( number )
|
||||
TF_BUILTIN(GlobalIsNaN, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Label return_true(this), return_false(this);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_num(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_num);
|
||||
var_num.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
Node* num = var_num.value();
|
||||
|
||||
// Check if {num} is a Smi or a HeapObject.
|
||||
GotoIf(TaggedIsSmi(num), &return_false);
|
||||
|
||||
// Check if {num} is a HeapNumber.
|
||||
Label if_numisheapnumber(this),
|
||||
if_numisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(num)), &if_numisheapnumber,
|
||||
&if_numisnotheapnumber);
|
||||
|
||||
Bind(&if_numisheapnumber);
|
||||
{
|
||||
// Check if {num} contains a NaN.
|
||||
Node* num_value = LoadHeapNumberValue(num);
|
||||
BranchIfFloat64IsNaN(num_value, &return_true, &return_false);
|
||||
}
|
||||
|
||||
Bind(&if_numisnotheapnumber);
|
||||
{
|
||||
// Need to convert {num} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_num.Bind(CallStub(callable, context, num));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&return_true);
|
||||
Return(BooleanConstant(true));
|
||||
|
||||
Bind(&return_false);
|
||||
Return(BooleanConstant(false));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -5,7 +5,6 @@
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/compiler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/objects-inl.h"
|
||||
@ -103,100 +102,5 @@ BUILTIN(GlobalEval) {
|
||||
Execution::Call(isolate, function, target_global_proxy, 0, nullptr));
|
||||
}
|
||||
|
||||
// ES6 section 18.2.2 isFinite ( number )
|
||||
TF_BUILTIN(GlobalIsFinite, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Label return_true(this), return_false(this);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_num(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_num);
|
||||
var_num.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
Node* num = var_num.value();
|
||||
|
||||
// Check if {num} is a Smi or a HeapObject.
|
||||
GotoIf(TaggedIsSmi(num), &return_true);
|
||||
|
||||
// Check if {num} is a HeapNumber.
|
||||
Label if_numisheapnumber(this),
|
||||
if_numisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(num)), &if_numisheapnumber,
|
||||
&if_numisnotheapnumber);
|
||||
|
||||
Bind(&if_numisheapnumber);
|
||||
{
|
||||
// Check if {num} contains a finite, non-NaN value.
|
||||
Node* num_value = LoadHeapNumberValue(num);
|
||||
BranchIfFloat64IsNaN(Float64Sub(num_value, num_value), &return_false,
|
||||
&return_true);
|
||||
}
|
||||
|
||||
Bind(&if_numisnotheapnumber);
|
||||
{
|
||||
// Need to convert {num} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_num.Bind(CallStub(callable, context, num));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&return_true);
|
||||
Return(BooleanConstant(true));
|
||||
|
||||
Bind(&return_false);
|
||||
Return(BooleanConstant(false));
|
||||
}
|
||||
|
||||
// ES6 section 18.2.3 isNaN ( number )
|
||||
TF_BUILTIN(GlobalIsNaN, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Label return_true(this), return_false(this);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_num(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_num);
|
||||
var_num.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
Node* num = var_num.value();
|
||||
|
||||
// Check if {num} is a Smi or a HeapObject.
|
||||
GotoIf(TaggedIsSmi(num), &return_false);
|
||||
|
||||
// Check if {num} is a HeapNumber.
|
||||
Label if_numisheapnumber(this),
|
||||
if_numisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(num)), &if_numisheapnumber,
|
||||
&if_numisnotheapnumber);
|
||||
|
||||
Bind(&if_numisheapnumber);
|
||||
{
|
||||
// Check if {num} contains a NaN.
|
||||
Node* num_value = LoadHeapNumberValue(num);
|
||||
BranchIfFloat64IsNaN(num_value, &return_true, &return_false);
|
||||
}
|
||||
|
||||
Bind(&if_numisnotheapnumber);
|
||||
{
|
||||
// Need to convert {num} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_num.Bind(CallStub(callable, context, num));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&return_true);
|
||||
Return(BooleanConstant(true));
|
||||
|
||||
Bind(&return_false);
|
||||
Return(BooleanConstant(false));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/ic/handler-compiler.h"
|
@ -2,10 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/ic/accessor-assembler.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
174
src/builtins/builtins-internal-gen.cc
Normal file
174
src/builtins/builtins-internal-gen.cc
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Interrupt and stack checks.
|
||||
|
||||
void Builtins::Generate_InterruptCheck(MacroAssembler* masm) {
|
||||
masm->TailCallRuntime(Runtime::kInterrupt);
|
||||
}
|
||||
|
||||
void Builtins::Generate_StackCheck(MacroAssembler* masm) {
|
||||
masm->TailCallRuntime(Runtime::kStackGuard);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// TurboFan support builtins.
|
||||
|
||||
TF_BUILTIN(CopyFastSmiOrObjectElements, CodeStubAssembler) {
|
||||
typedef CopyFastSmiOrObjectElementsDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
|
||||
// Load the {object}s elements.
|
||||
Node* source = LoadObjectField(object, JSObject::kElementsOffset);
|
||||
|
||||
ParameterMode mode = OptimalParameterMode();
|
||||
Node* length = TaggedToParameter(LoadFixedArrayBaseLength(source), mode);
|
||||
|
||||
// Check if we can allocate in new space.
|
||||
ElementsKind kind = FAST_ELEMENTS;
|
||||
int max_elements = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(kind);
|
||||
Label if_newspace(this), if_oldspace(this);
|
||||
Branch(UintPtrOrSmiLessThan(length, IntPtrOrSmiConstant(max_elements, mode),
|
||||
mode),
|
||||
&if_newspace, &if_oldspace);
|
||||
|
||||
Bind(&if_newspace);
|
||||
{
|
||||
Node* target = AllocateFixedArray(kind, length, mode);
|
||||
CopyFixedArrayElements(kind, source, target, length, SKIP_WRITE_BARRIER,
|
||||
mode);
|
||||
StoreObjectField(object, JSObject::kElementsOffset, target);
|
||||
Return(target);
|
||||
}
|
||||
|
||||
Bind(&if_oldspace);
|
||||
{
|
||||
Node* target = AllocateFixedArray(kind, length, mode, kPretenured);
|
||||
CopyFixedArrayElements(kind, source, target, length, UPDATE_WRITE_BARRIER,
|
||||
mode);
|
||||
StoreObjectField(object, JSObject::kElementsOffset, target);
|
||||
Return(target);
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(GrowFastDoubleElements, CodeStubAssembler) {
|
||||
typedef GrowArrayElementsDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* key = Parameter(Descriptor::kKey);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label runtime(this, Label::kDeferred);
|
||||
Node* elements = LoadElements(object);
|
||||
elements = TryGrowElementsCapacity(object, elements, FAST_DOUBLE_ELEMENTS,
|
||||
key, &runtime);
|
||||
Return(elements);
|
||||
|
||||
Bind(&runtime);
|
||||
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
|
||||
}
|
||||
|
||||
TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) {
|
||||
typedef GrowArrayElementsDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* key = Parameter(Descriptor::kKey);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label runtime(this, Label::kDeferred);
|
||||
Node* elements = LoadElements(object);
|
||||
elements =
|
||||
TryGrowElementsCapacity(object, elements, FAST_ELEMENTS, key, &runtime);
|
||||
Return(elements);
|
||||
|
||||
Bind(&runtime);
|
||||
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
|
||||
}
|
||||
|
||||
TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
|
||||
typedef NewArgumentsElementsDescriptor Descriptor;
|
||||
|
||||
Node* frame = Parameter(Descriptor::kFrame);
|
||||
Node* length = SmiToWord(Parameter(Descriptor::kLength));
|
||||
|
||||
// Check if we can allocate in new space.
|
||||
ElementsKind kind = FAST_ELEMENTS;
|
||||
int max_elements = FixedArray::GetMaxLengthForNewSpaceAllocation(kind);
|
||||
Label if_newspace(this), if_oldspace(this, Label::kDeferred);
|
||||
Branch(IntPtrLessThan(length, IntPtrConstant(max_elements)), &if_newspace,
|
||||
&if_oldspace);
|
||||
|
||||
Bind(&if_newspace);
|
||||
{
|
||||
// Prefer EmptyFixedArray in case of non-positive {length} (the {length}
|
||||
// can be negative here for rest parameters).
|
||||
Label if_empty(this), if_notempty(this);
|
||||
Branch(IntPtrLessThanOrEqual(length, IntPtrConstant(0)), &if_empty,
|
||||
&if_notempty);
|
||||
|
||||
Bind(&if_empty);
|
||||
Return(EmptyFixedArrayConstant());
|
||||
|
||||
Bind(&if_notempty);
|
||||
{
|
||||
// Allocate a FixedArray in new space.
|
||||
Node* result = AllocateFixedArray(kind, length);
|
||||
|
||||
// Compute the effective {offset} into the {frame}.
|
||||
Node* offset = IntPtrAdd(length, IntPtrConstant(1));
|
||||
|
||||
// Copy the parameters from {frame} (starting at {offset}) to {result}.
|
||||
Variable var_index(this, MachineType::PointerRepresentation());
|
||||
Label loop(this, &var_index), done_loop(this);
|
||||
var_index.Bind(IntPtrConstant(0));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {index}.
|
||||
Node* index = var_index.value();
|
||||
|
||||
// Check if we are done.
|
||||
GotoIf(WordEqual(index, length), &done_loop);
|
||||
|
||||
// Load the parameter at the given {index}.
|
||||
Node* value = Load(MachineType::AnyTagged(), frame,
|
||||
WordShl(IntPtrSub(offset, index),
|
||||
IntPtrConstant(kPointerSizeLog2)));
|
||||
|
||||
// Store the {value} into the {result}.
|
||||
StoreFixedArrayElement(result, index, value, SKIP_WRITE_BARRIER);
|
||||
|
||||
// Continue with next {index}.
|
||||
var_index.Bind(IntPtrAdd(index, IntPtrConstant(1)));
|
||||
Goto(&loop);
|
||||
}
|
||||
|
||||
Bind(&done_loop);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_oldspace);
|
||||
{
|
||||
// Allocate in old space (or large object space).
|
||||
TailCallRuntime(Runtime::kNewArgumentsElements, NoContextConstant(),
|
||||
BitcastWordToTagged(frame), SmiFromWord(length));
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(ReturnReceiver, CodeStubAssembler) { Return(Parameter(0)); }
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -4,10 +4,8 @@
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/interface-descriptors.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -42,164 +40,5 @@ BUILTIN(RestrictedStrictArgumentsPropertiesThrower) {
|
||||
isolate, NewTypeError(MessageTemplate::kStrictPoisonPill));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Interrupt and stack checks.
|
||||
|
||||
void Builtins::Generate_InterruptCheck(MacroAssembler* masm) {
|
||||
masm->TailCallRuntime(Runtime::kInterrupt);
|
||||
}
|
||||
|
||||
void Builtins::Generate_StackCheck(MacroAssembler* masm) {
|
||||
masm->TailCallRuntime(Runtime::kStackGuard);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// TurboFan support builtins.
|
||||
|
||||
TF_BUILTIN(CopyFastSmiOrObjectElements, CodeStubAssembler) {
|
||||
typedef CopyFastSmiOrObjectElementsDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
|
||||
// Load the {object}s elements.
|
||||
Node* source = LoadObjectField(object, JSObject::kElementsOffset);
|
||||
|
||||
ParameterMode mode = OptimalParameterMode();
|
||||
Node* length = TaggedToParameter(LoadFixedArrayBaseLength(source), mode);
|
||||
|
||||
// Check if we can allocate in new space.
|
||||
ElementsKind kind = FAST_ELEMENTS;
|
||||
int max_elements = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(kind);
|
||||
Label if_newspace(this), if_oldspace(this);
|
||||
Branch(UintPtrOrSmiLessThan(length, IntPtrOrSmiConstant(max_elements, mode),
|
||||
mode),
|
||||
&if_newspace, &if_oldspace);
|
||||
|
||||
Bind(&if_newspace);
|
||||
{
|
||||
Node* target = AllocateFixedArray(kind, length, mode);
|
||||
CopyFixedArrayElements(kind, source, target, length, SKIP_WRITE_BARRIER,
|
||||
mode);
|
||||
StoreObjectField(object, JSObject::kElementsOffset, target);
|
||||
Return(target);
|
||||
}
|
||||
|
||||
Bind(&if_oldspace);
|
||||
{
|
||||
Node* target = AllocateFixedArray(kind, length, mode, kPretenured);
|
||||
CopyFixedArrayElements(kind, source, target, length, UPDATE_WRITE_BARRIER,
|
||||
mode);
|
||||
StoreObjectField(object, JSObject::kElementsOffset, target);
|
||||
Return(target);
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(GrowFastDoubleElements, CodeStubAssembler) {
|
||||
typedef GrowArrayElementsDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* key = Parameter(Descriptor::kKey);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label runtime(this, Label::kDeferred);
|
||||
Node* elements = LoadElements(object);
|
||||
elements = TryGrowElementsCapacity(object, elements, FAST_DOUBLE_ELEMENTS,
|
||||
key, &runtime);
|
||||
Return(elements);
|
||||
|
||||
Bind(&runtime);
|
||||
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
|
||||
}
|
||||
|
||||
TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) {
|
||||
typedef GrowArrayElementsDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* key = Parameter(Descriptor::kKey);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label runtime(this, Label::kDeferred);
|
||||
Node* elements = LoadElements(object);
|
||||
elements =
|
||||
TryGrowElementsCapacity(object, elements, FAST_ELEMENTS, key, &runtime);
|
||||
Return(elements);
|
||||
|
||||
Bind(&runtime);
|
||||
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
|
||||
}
|
||||
|
||||
TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
|
||||
typedef NewArgumentsElementsDescriptor Descriptor;
|
||||
|
||||
Node* frame = Parameter(Descriptor::kFrame);
|
||||
Node* length = SmiToWord(Parameter(Descriptor::kLength));
|
||||
|
||||
// Check if we can allocate in new space.
|
||||
ElementsKind kind = FAST_ELEMENTS;
|
||||
int max_elements = FixedArray::GetMaxLengthForNewSpaceAllocation(kind);
|
||||
Label if_newspace(this), if_oldspace(this, Label::kDeferred);
|
||||
Branch(IntPtrLessThan(length, IntPtrConstant(max_elements)), &if_newspace,
|
||||
&if_oldspace);
|
||||
|
||||
Bind(&if_newspace);
|
||||
{
|
||||
// Prefer EmptyFixedArray in case of non-positive {length} (the {length}
|
||||
// can be negative here for rest parameters).
|
||||
Label if_empty(this), if_notempty(this);
|
||||
Branch(IntPtrLessThanOrEqual(length, IntPtrConstant(0)), &if_empty,
|
||||
&if_notempty);
|
||||
|
||||
Bind(&if_empty);
|
||||
Return(EmptyFixedArrayConstant());
|
||||
|
||||
Bind(&if_notempty);
|
||||
{
|
||||
// Allocate a FixedArray in new space.
|
||||
Node* result = AllocateFixedArray(kind, length);
|
||||
|
||||
// Compute the effective {offset} into the {frame}.
|
||||
Node* offset = IntPtrAdd(length, IntPtrConstant(1));
|
||||
|
||||
// Copy the parameters from {frame} (starting at {offset}) to {result}.
|
||||
Variable var_index(this, MachineType::PointerRepresentation());
|
||||
Label loop(this, &var_index), done_loop(this);
|
||||
var_index.Bind(IntPtrConstant(0));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {index}.
|
||||
Node* index = var_index.value();
|
||||
|
||||
// Check if we are done.
|
||||
GotoIf(WordEqual(index, length), &done_loop);
|
||||
|
||||
// Load the parameter at the given {index}.
|
||||
Node* value = Load(MachineType::AnyTagged(), frame,
|
||||
WordShl(IntPtrSub(offset, index),
|
||||
IntPtrConstant(kPointerSizeLog2)));
|
||||
|
||||
// Store the {value} into the {result}.
|
||||
StoreFixedArrayElement(result, index, value, SKIP_WRITE_BARRIER);
|
||||
|
||||
// Continue with next {index}.
|
||||
var_index.Bind(IntPtrAdd(index, IntPtrConstant(1)));
|
||||
Goto(&loop);
|
||||
}
|
||||
|
||||
Bind(&done_loop);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_oldspace);
|
||||
{
|
||||
// Allocate in old space (or large object space).
|
||||
TailCallRuntime(Runtime::kNewArgumentsElements, NoContextConstant(),
|
||||
BitcastWordToTagged(frame), SmiFromWord(length));
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(ReturnReceiver, CodeStubAssembler) { Return(Parameter(0)); }
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
476
src/builtins/builtins-math-gen.cc
Normal file
476
src/builtins/builtins-math-gen.cc
Normal file
@ -0,0 +1,476 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 20.2.2 Function Properties of the Math Object
|
||||
|
||||
class MathBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit MathBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void MathRoundingOperation(Node* (CodeStubAssembler::*float64op)(Node*));
|
||||
void MathUnaryOperation(Node* (CodeStubAssembler::*float64op)(Node*));
|
||||
void MathMaxMin(Node* (CodeStubAssembler::*float64op)(Node*, Node*),
|
||||
double default_val);
|
||||
};
|
||||
|
||||
// ES6 section - 20.2.2.1 Math.abs ( x )
|
||||
TF_BUILTIN(MathAbs, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_x);
|
||||
var_x.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(this), if_xisnotsmi(this);
|
||||
Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
Bind(&if_xissmi);
|
||||
{
|
||||
// Check if {x} is already positive.
|
||||
Label if_xispositive(this), if_xisnotpositive(this);
|
||||
BranchIfSmiLessThanOrEqual(SmiConstant(Smi::FromInt(0)), x,
|
||||
&if_xispositive, &if_xisnotpositive);
|
||||
|
||||
Bind(&if_xispositive);
|
||||
{
|
||||
// Just return the input {x}.
|
||||
Return(x);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotpositive);
|
||||
{
|
||||
// Try to negate the {x} value.
|
||||
Node* pair =
|
||||
IntPtrSubWithOverflow(IntPtrConstant(0), BitcastTaggedToWord(x));
|
||||
Node* overflow = Projection(1, pair);
|
||||
Label if_overflow(this, Label::kDeferred), if_notoverflow(this);
|
||||
Branch(overflow, &if_overflow, &if_notoverflow);
|
||||
|
||||
Bind(&if_notoverflow);
|
||||
{
|
||||
// There is a Smi representation for negated {x}.
|
||||
Node* result = Projection(0, pair);
|
||||
Return(BitcastWordToTagged(result));
|
||||
}
|
||||
|
||||
Bind(&if_overflow);
|
||||
{ Return(NumberConstant(0.0 - Smi::kMinValue)); }
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
|
||||
&if_xisnotheapnumber);
|
||||
|
||||
Bind(&if_xisheapnumber);
|
||||
{
|
||||
Node* x_value = LoadHeapNumberValue(x);
|
||||
Node* value = Float64Abs(x_value);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_x.Bind(CallStub(callable, context, x));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MathBuiltinsAssembler::MathRoundingOperation(
|
||||
Node* (CodeStubAssembler::*float64op)(Node*)) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_x);
|
||||
var_x.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(this), if_xisnotsmi(this);
|
||||
Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
Bind(&if_xissmi);
|
||||
{
|
||||
// Nothing to do when {x} is a Smi.
|
||||
Return(x);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
|
||||
&if_xisnotheapnumber);
|
||||
|
||||
Bind(&if_xisheapnumber);
|
||||
{
|
||||
Node* x_value = LoadHeapNumberValue(x);
|
||||
Node* value = (this->*float64op)(x_value);
|
||||
Node* result = ChangeFloat64ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_x.Bind(CallStub(callable, context, x));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MathBuiltinsAssembler::MathUnaryOperation(
|
||||
Node* (CodeStubAssembler::*float64op)(Node*)) {
|
||||
Node* x = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* value = (this->*float64op)(x_value);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
void MathBuiltinsAssembler::MathMaxMin(
|
||||
Node* (CodeStubAssembler::*float64op)(Node*, Node*), double default_val) {
|
||||
Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
|
||||
Node* context = Parameter(BuiltinDescriptor::kContext);
|
||||
|
||||
CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
|
||||
argc = arguments.GetLength();
|
||||
|
||||
Variable result(this, MachineRepresentation::kFloat64);
|
||||
result.Bind(Float64Constant(default_val));
|
||||
|
||||
CodeStubAssembler::VariableList vars({&result}, zone());
|
||||
arguments.ForEach(vars, [this, float64op, context, &result](Node* arg) {
|
||||
Node* float_value = TruncateTaggedToFloat64(context, arg);
|
||||
result.Bind((this->*float64op)(result.value(), float_value));
|
||||
});
|
||||
|
||||
arguments.PopAndReturn(ChangeFloat64ToTagged(result.value()));
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.2 Math.acos ( x )
|
||||
TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Acos);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.3 Math.acosh ( x )
|
||||
TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Acosh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.4 Math.asin ( x )
|
||||
TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Asin);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.5 Math.asinh ( x )
|
||||
TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Asinh);
|
||||
}
|
||||
// ES6 section 20.2.2.6 Math.atan ( x )
|
||||
TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Atan);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.7 Math.atanh ( x )
|
||||
TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Atanh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.8 Math.atan2 ( y, x )
|
||||
TF_BUILTIN(MathAtan2, CodeStubAssembler) {
|
||||
Node* y = Parameter(1);
|
||||
Node* x = Parameter(2);
|
||||
Node* context = Parameter(5);
|
||||
|
||||
Node* y_value = TruncateTaggedToFloat64(context, y);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* value = Float64Atan2(y_value, x_value);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.10 Math.ceil ( x )
|
||||
TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Ceil);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.9 Math.cbrt ( x )
|
||||
TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Cbrt);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.11 Math.clz32 ( x )
|
||||
TF_BUILTIN(MathClz32, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// Shared entry point for the clz32 operation.
|
||||
Variable var_clz32_x(this, MachineRepresentation::kWord32);
|
||||
Label do_clz32(this);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_x);
|
||||
var_x.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(this), if_xisnotsmi(this);
|
||||
Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
Bind(&if_xissmi);
|
||||
{
|
||||
var_clz32_x.Bind(SmiToWord32(x));
|
||||
Goto(&do_clz32);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
|
||||
&if_xisnotheapnumber);
|
||||
|
||||
Bind(&if_xisheapnumber);
|
||||
{
|
||||
var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x));
|
||||
Goto(&do_clz32);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_x.Bind(CallStub(callable, context, x));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&do_clz32);
|
||||
{
|
||||
Node* x_value = var_clz32_x.value();
|
||||
Node* value = Word32Clz(x_value);
|
||||
Node* result = ChangeInt32ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.12 Math.cos ( x )
|
||||
TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Cos);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.13 Math.cosh ( x )
|
||||
TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Cosh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.14 Math.exp ( x )
|
||||
TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Exp);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.15 Math.expm1 ( x )
|
||||
TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Expm1);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.16 Math.floor ( x )
|
||||
TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Floor);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.17 Math.fround ( x )
|
||||
TF_BUILTIN(MathFround, CodeStubAssembler) {
|
||||
Node* x = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* value32 = TruncateFloat64ToFloat32(x_value);
|
||||
Node* value = ChangeFloat32ToFloat64(value32);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.19 Math.imul ( x, y )
|
||||
TF_BUILTIN(MathImul, CodeStubAssembler) {
|
||||
Node* x = Parameter(1);
|
||||
Node* y = Parameter(2);
|
||||
Node* context = Parameter(5);
|
||||
Node* x_value = TruncateTaggedToWord32(context, x);
|
||||
Node* y_value = TruncateTaggedToWord32(context, y);
|
||||
Node* value = Int32Mul(x_value, y_value);
|
||||
Node* result = ChangeInt32ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.20 Math.log ( x )
|
||||
TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.21 Math.log1p ( x )
|
||||
TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log1p);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.22 Math.log10 ( x )
|
||||
TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log10);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.23 Math.log2 ( x )
|
||||
TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log2);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.26 Math.pow ( x, y )
|
||||
TF_BUILTIN(MathPow, CodeStubAssembler) {
|
||||
Node* x = Parameter(1);
|
||||
Node* y = Parameter(2);
|
||||
Node* context = Parameter(5);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* y_value = TruncateTaggedToFloat64(context, y);
|
||||
Node* value = Float64Pow(x_value, y_value);
|
||||
Node* result = ChangeFloat64ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.27 Math.random ( )
|
||||
TF_BUILTIN(MathRandom, CodeStubAssembler) {
|
||||
Node* context = Parameter(3);
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
|
||||
// Load cache index.
|
||||
Variable smi_index(this, MachineRepresentation::kTagged);
|
||||
smi_index.Bind(
|
||||
LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));
|
||||
|
||||
// Cached random numbers are exhausted if index is 0. Go to slow path.
|
||||
Label if_cached(this);
|
||||
GotoIf(SmiAbove(smi_index.value(), SmiConstant(Smi::kZero)), &if_cached);
|
||||
|
||||
// Cache exhausted, populate the cache. Return value is the new index.
|
||||
smi_index.Bind(CallRuntime(Runtime::kGenerateRandomNumbers, context));
|
||||
Goto(&if_cached);
|
||||
|
||||
// Compute next index by decrement.
|
||||
Bind(&if_cached);
|
||||
Node* new_smi_index = SmiSub(smi_index.value(), SmiConstant(Smi::FromInt(1)));
|
||||
StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
|
||||
new_smi_index);
|
||||
|
||||
// Load and return next cached random number.
|
||||
Node* array =
|
||||
LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
|
||||
Node* random = LoadFixedDoubleArrayElement(
|
||||
array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
|
||||
Return(AllocateHeapNumberWithValue(random));
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.28 Math.round ( x )
|
||||
TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Round);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.29 Math.sign ( x )
|
||||
TF_BUILTIN(MathSign, CodeStubAssembler) {
|
||||
// Convert the {x} value to a Number.
|
||||
Node* x = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
|
||||
// Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
|
||||
Label if_xisnegative(this), if_xispositive(this);
|
||||
GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative);
|
||||
GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive);
|
||||
Return(ChangeFloat64ToTagged(x_value));
|
||||
|
||||
Bind(&if_xisnegative);
|
||||
Return(SmiConstant(Smi::FromInt(-1)));
|
||||
|
||||
Bind(&if_xispositive);
|
||||
Return(SmiConstant(Smi::FromInt(1)));
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.30 Math.sin ( x )
|
||||
TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Sin);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.31 Math.sinh ( x )
|
||||
TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Sinh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.32 Math.sqrt ( x )
|
||||
TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Sqrt);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.33 Math.tan ( x )
|
||||
TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Tan);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.34 Math.tanh ( x )
|
||||
TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Tanh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.35 Math.trunc ( x )
|
||||
TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Trunc);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.24 Math.max ( value1, value2 , ...values )
|
||||
TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
|
||||
MathMaxMin(&CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.25 Math.min ( value1, value2 , ...values )
|
||||
TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
|
||||
MathMaxMin(&CodeStubAssembler::Float64Min, V8_INFINITY);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -4,8 +4,6 @@
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
@ -15,324 +13,6 @@ namespace internal {
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 20.2.2 Function Properties of the Math Object
|
||||
|
||||
class MathBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit MathBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void MathRoundingOperation(Node* (CodeStubAssembler::*float64op)(Node*));
|
||||
void MathUnaryOperation(Node* (CodeStubAssembler::*float64op)(Node*));
|
||||
void MathMaxMin(Node* (CodeStubAssembler::*float64op)(Node*, Node*),
|
||||
double default_val);
|
||||
};
|
||||
|
||||
// ES6 section - 20.2.2.1 Math.abs ( x )
|
||||
TF_BUILTIN(MathAbs, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_x);
|
||||
var_x.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(this), if_xisnotsmi(this);
|
||||
Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
Bind(&if_xissmi);
|
||||
{
|
||||
// Check if {x} is already positive.
|
||||
Label if_xispositive(this), if_xisnotpositive(this);
|
||||
BranchIfSmiLessThanOrEqual(SmiConstant(Smi::FromInt(0)), x,
|
||||
&if_xispositive, &if_xisnotpositive);
|
||||
|
||||
Bind(&if_xispositive);
|
||||
{
|
||||
// Just return the input {x}.
|
||||
Return(x);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotpositive);
|
||||
{
|
||||
// Try to negate the {x} value.
|
||||
Node* pair =
|
||||
IntPtrSubWithOverflow(IntPtrConstant(0), BitcastTaggedToWord(x));
|
||||
Node* overflow = Projection(1, pair);
|
||||
Label if_overflow(this, Label::kDeferred), if_notoverflow(this);
|
||||
Branch(overflow, &if_overflow, &if_notoverflow);
|
||||
|
||||
Bind(&if_notoverflow);
|
||||
{
|
||||
// There is a Smi representation for negated {x}.
|
||||
Node* result = Projection(0, pair);
|
||||
Return(BitcastWordToTagged(result));
|
||||
}
|
||||
|
||||
Bind(&if_overflow);
|
||||
{ Return(NumberConstant(0.0 - Smi::kMinValue)); }
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
|
||||
&if_xisnotheapnumber);
|
||||
|
||||
Bind(&if_xisheapnumber);
|
||||
{
|
||||
Node* x_value = LoadHeapNumberValue(x);
|
||||
Node* value = Float64Abs(x_value);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_x.Bind(CallStub(callable, context, x));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MathBuiltinsAssembler::MathRoundingOperation(
|
||||
Node* (CodeStubAssembler::*float64op)(Node*)) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_x);
|
||||
var_x.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(this), if_xisnotsmi(this);
|
||||
Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
Bind(&if_xissmi);
|
||||
{
|
||||
// Nothing to do when {x} is a Smi.
|
||||
Return(x);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
|
||||
&if_xisnotheapnumber);
|
||||
|
||||
Bind(&if_xisheapnumber);
|
||||
{
|
||||
Node* x_value = LoadHeapNumberValue(x);
|
||||
Node* value = (this->*float64op)(x_value);
|
||||
Node* result = ChangeFloat64ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_x.Bind(CallStub(callable, context, x));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MathBuiltinsAssembler::MathUnaryOperation(
|
||||
Node* (CodeStubAssembler::*float64op)(Node*)) {
|
||||
Node* x = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* value = (this->*float64op)(x_value);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
void MathBuiltinsAssembler::MathMaxMin(
|
||||
Node* (CodeStubAssembler::*float64op)(Node*, Node*), double default_val) {
|
||||
Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
|
||||
Node* context = Parameter(BuiltinDescriptor::kContext);
|
||||
|
||||
CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
|
||||
argc = arguments.GetLength();
|
||||
|
||||
Variable result(this, MachineRepresentation::kFloat64);
|
||||
result.Bind(Float64Constant(default_val));
|
||||
|
||||
CodeStubAssembler::VariableList vars({&result}, zone());
|
||||
arguments.ForEach(vars, [this, float64op, context, &result](Node* arg) {
|
||||
Node* float_value = TruncateTaggedToFloat64(context, arg);
|
||||
result.Bind((this->*float64op)(result.value(), float_value));
|
||||
});
|
||||
|
||||
arguments.PopAndReturn(ChangeFloat64ToTagged(result.value()));
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.2 Math.acos ( x )
|
||||
TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Acos);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.3 Math.acosh ( x )
|
||||
TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Acosh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.4 Math.asin ( x )
|
||||
TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Asin);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.5 Math.asinh ( x )
|
||||
TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Asinh);
|
||||
}
|
||||
// ES6 section 20.2.2.6 Math.atan ( x )
|
||||
TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Atan);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.7 Math.atanh ( x )
|
||||
TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Atanh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.8 Math.atan2 ( y, x )
|
||||
TF_BUILTIN(MathAtan2, CodeStubAssembler) {
|
||||
Node* y = Parameter(1);
|
||||
Node* x = Parameter(2);
|
||||
Node* context = Parameter(5);
|
||||
|
||||
Node* y_value = TruncateTaggedToFloat64(context, y);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* value = Float64Atan2(y_value, x_value);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.10 Math.ceil ( x )
|
||||
TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Ceil);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.9 Math.cbrt ( x )
|
||||
TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Cbrt);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.11 Math.clz32 ( x )
|
||||
TF_BUILTIN(MathClz32, CodeStubAssembler) {
|
||||
Node* context = Parameter(4);
|
||||
|
||||
// Shared entry point for the clz32 operation.
|
||||
Variable var_clz32_x(this, MachineRepresentation::kWord32);
|
||||
Label do_clz32(this);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(this, MachineRepresentation::kTagged);
|
||||
Label loop(this, &var_x);
|
||||
var_x.Bind(Parameter(1));
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(this), if_xisnotsmi(this);
|
||||
Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
Bind(&if_xissmi);
|
||||
{
|
||||
var_clz32_x.Bind(SmiToWord32(x));
|
||||
Goto(&do_clz32);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
|
||||
Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
|
||||
&if_xisnotheapnumber);
|
||||
|
||||
Bind(&if_xisheapnumber);
|
||||
{
|
||||
var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x));
|
||||
Goto(&do_clz32);
|
||||
}
|
||||
|
||||
Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable = CodeFactory::NonNumberToNumber(isolate());
|
||||
var_x.Bind(CallStub(callable, context, x));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&do_clz32);
|
||||
{
|
||||
Node* x_value = var_clz32_x.value();
|
||||
Node* value = Word32Clz(x_value);
|
||||
Node* result = ChangeInt32ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.12 Math.cos ( x )
|
||||
TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Cos);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.13 Math.cosh ( x )
|
||||
TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Cosh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.14 Math.exp ( x )
|
||||
TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Exp);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.15 Math.expm1 ( x )
|
||||
TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Expm1);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.16 Math.floor ( x )
|
||||
TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Floor);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.17 Math.fround ( x )
|
||||
TF_BUILTIN(MathFround, CodeStubAssembler) {
|
||||
Node* x = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* value32 = TruncateFloat64ToFloat32(x_value);
|
||||
Node* value = ChangeFloat32ToFloat64(value32);
|
||||
Node* result = AllocateHeapNumberWithValue(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values )
|
||||
BUILTIN(MathHypot) {
|
||||
HandleScope scope(isolate);
|
||||
@ -385,146 +65,5 @@ BUILTIN(MathHypot) {
|
||||
return *isolate->factory()->NewNumber(std::sqrt(sum) * max);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.19 Math.imul ( x, y )
|
||||
TF_BUILTIN(MathImul, CodeStubAssembler) {
|
||||
Node* x = Parameter(1);
|
||||
Node* y = Parameter(2);
|
||||
Node* context = Parameter(5);
|
||||
Node* x_value = TruncateTaggedToWord32(context, x);
|
||||
Node* y_value = TruncateTaggedToWord32(context, y);
|
||||
Node* value = Int32Mul(x_value, y_value);
|
||||
Node* result = ChangeInt32ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.20 Math.log ( x )
|
||||
TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.21 Math.log1p ( x )
|
||||
TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log1p);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.22 Math.log10 ( x )
|
||||
TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log10);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.23 Math.log2 ( x )
|
||||
TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Log2);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.26 Math.pow ( x, y )
|
||||
TF_BUILTIN(MathPow, CodeStubAssembler) {
|
||||
Node* x = Parameter(1);
|
||||
Node* y = Parameter(2);
|
||||
Node* context = Parameter(5);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
Node* y_value = TruncateTaggedToFloat64(context, y);
|
||||
Node* value = Float64Pow(x_value, y_value);
|
||||
Node* result = ChangeFloat64ToTagged(value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.27 Math.random ( )
|
||||
TF_BUILTIN(MathRandom, CodeStubAssembler) {
|
||||
Node* context = Parameter(3);
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
|
||||
// Load cache index.
|
||||
Variable smi_index(this, MachineRepresentation::kTagged);
|
||||
smi_index.Bind(
|
||||
LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));
|
||||
|
||||
// Cached random numbers are exhausted if index is 0. Go to slow path.
|
||||
Label if_cached(this);
|
||||
GotoIf(SmiAbove(smi_index.value(), SmiConstant(Smi::kZero)), &if_cached);
|
||||
|
||||
// Cache exhausted, populate the cache. Return value is the new index.
|
||||
smi_index.Bind(CallRuntime(Runtime::kGenerateRandomNumbers, context));
|
||||
Goto(&if_cached);
|
||||
|
||||
// Compute next index by decrement.
|
||||
Bind(&if_cached);
|
||||
Node* new_smi_index = SmiSub(smi_index.value(), SmiConstant(Smi::FromInt(1)));
|
||||
StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
|
||||
new_smi_index);
|
||||
|
||||
// Load and return next cached random number.
|
||||
Node* array =
|
||||
LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
|
||||
Node* random = LoadFixedDoubleArrayElement(
|
||||
array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
|
||||
Return(AllocateHeapNumberWithValue(random));
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.28 Math.round ( x )
|
||||
TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Round);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.29 Math.sign ( x )
|
||||
TF_BUILTIN(MathSign, CodeStubAssembler) {
|
||||
// Convert the {x} value to a Number.
|
||||
Node* x = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
Node* x_value = TruncateTaggedToFloat64(context, x);
|
||||
|
||||
// Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
|
||||
Label if_xisnegative(this), if_xispositive(this);
|
||||
GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative);
|
||||
GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive);
|
||||
Return(ChangeFloat64ToTagged(x_value));
|
||||
|
||||
Bind(&if_xisnegative);
|
||||
Return(SmiConstant(Smi::FromInt(-1)));
|
||||
|
||||
Bind(&if_xispositive);
|
||||
Return(SmiConstant(Smi::FromInt(1)));
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.30 Math.sin ( x )
|
||||
TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Sin);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.31 Math.sinh ( x )
|
||||
TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Sinh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.32 Math.sqrt ( x )
|
||||
TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Sqrt);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.33 Math.tan ( x )
|
||||
TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Tan);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.34 Math.tanh ( x )
|
||||
TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
|
||||
MathUnaryOperation(&CodeStubAssembler::Float64Tanh);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.35 Math.trunc ( x )
|
||||
TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
|
||||
MathRoundingOperation(&CodeStubAssembler::Float64Trunc);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.24 Math.max ( value1, value2 , ...values )
|
||||
TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
|
||||
MathMaxMin(&CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.25 Math.min ( value1, value2 , ...values )
|
||||
TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
|
||||
MathMaxMin(&CodeStubAssembler::Float64Min, V8_INFINITY);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
1388
src/builtins/builtins-number-gen.cc
Normal file
1388
src/builtins/builtins-number-gen.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
413
src/builtins/builtins-object-gen.cc
Normal file
413
src/builtins/builtins-object-gen.cc
Normal file
@ -0,0 +1,413 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 19.1 Object Objects
|
||||
|
||||
typedef compiler::Node Node;
|
||||
|
||||
class ObjectBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit ObjectBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void IsString(Node* object, Label* if_string, Label* if_notstring);
|
||||
void ReturnToStringFormat(Node* context, Node* string);
|
||||
};
|
||||
|
||||
void ObjectBuiltinsAssembler::IsString(Node* object, Label* if_string,
|
||||
Label* if_notstring) {
|
||||
Label if_notsmi(this);
|
||||
Branch(TaggedIsSmi(object), if_notstring, &if_notsmi);
|
||||
|
||||
Bind(&if_notsmi);
|
||||
{
|
||||
Node* instance_type = LoadInstanceType(object);
|
||||
|
||||
Branch(IsStringInstanceType(instance_type), if_string, if_notstring);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectBuiltinsAssembler::ReturnToStringFormat(Node* context,
|
||||
Node* string) {
|
||||
Node* lhs = HeapConstant(factory()->NewStringFromStaticChars("[object "));
|
||||
Node* rhs = HeapConstant(factory()->NewStringFromStaticChars("]"));
|
||||
|
||||
Callable callable =
|
||||
CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
|
||||
|
||||
Return(CallStub(callable, context, CallStub(callable, context, lhs, string),
|
||||
rhs));
|
||||
}
|
||||
|
||||
TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) {
|
||||
Node* object = Parameter(0);
|
||||
Node* key = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Label call_runtime(this), return_true(this), return_false(this);
|
||||
|
||||
// Smi receivers do not have own properties.
|
||||
Label if_objectisnotsmi(this);
|
||||
Branch(TaggedIsSmi(object), &return_false, &if_objectisnotsmi);
|
||||
Bind(&if_objectisnotsmi);
|
||||
|
||||
Node* map = LoadMap(object);
|
||||
Node* instance_type = LoadMapInstanceType(map);
|
||||
|
||||
{
|
||||
Variable var_index(this, MachineType::PointerRepresentation());
|
||||
Variable var_unique(this, MachineRepresentation::kTagged);
|
||||
|
||||
Label keyisindex(this), if_iskeyunique(this);
|
||||
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &var_unique,
|
||||
&call_runtime);
|
||||
|
||||
Bind(&if_iskeyunique);
|
||||
TryHasOwnProperty(object, map, instance_type, var_unique.value(),
|
||||
&return_true, &return_false, &call_runtime);
|
||||
|
||||
Bind(&keyisindex);
|
||||
// Handle negative keys in the runtime.
|
||||
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)), &call_runtime);
|
||||
TryLookupElement(object, map, instance_type, var_index.value(),
|
||||
&return_true, &return_false, &call_runtime);
|
||||
}
|
||||
Bind(&return_true);
|
||||
Return(BooleanConstant(true));
|
||||
|
||||
Bind(&return_false);
|
||||
Return(BooleanConstant(false));
|
||||
|
||||
Bind(&call_runtime);
|
||||
Return(CallRuntime(Runtime::kObjectHasOwnProperty, context, object, key));
|
||||
}
|
||||
|
||||
// ES6 section 19.1.3.6 Object.prototype.toString
|
||||
TF_BUILTIN(ObjectProtoToString, ObjectBuiltinsAssembler) {
|
||||
Label return_undefined(this, Label::kDeferred),
|
||||
return_null(this, Label::kDeferred),
|
||||
return_arguments(this, Label::kDeferred), return_array(this),
|
||||
return_api(this, Label::kDeferred), return_object(this),
|
||||
return_regexp(this), return_function(this), return_error(this),
|
||||
return_date(this), return_jsvalue(this),
|
||||
return_jsproxy(this, Label::kDeferred);
|
||||
|
||||
Label if_isproxy(this, Label::kDeferred);
|
||||
|
||||
Label checkstringtag(this);
|
||||
Label if_tostringtag(this), if_notostringtag(this);
|
||||
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
GotoIf(WordEqual(receiver, UndefinedConstant()), &return_undefined);
|
||||
|
||||
GotoIf(WordEqual(receiver, NullConstant()), &return_null);
|
||||
|
||||
Callable to_object = CodeFactory::ToObject(isolate());
|
||||
receiver = CallStub(to_object, context, receiver);
|
||||
|
||||
Node* receiver_instance_type = LoadInstanceType(receiver);
|
||||
|
||||
// for proxies, check IsArray before getting @@toStringTag
|
||||
Variable var_proxy_is_array(this, MachineRepresentation::kTagged);
|
||||
var_proxy_is_array.Bind(BooleanConstant(false));
|
||||
|
||||
Branch(Word32Equal(receiver_instance_type, Int32Constant(JS_PROXY_TYPE)),
|
||||
&if_isproxy, &checkstringtag);
|
||||
|
||||
Bind(&if_isproxy);
|
||||
{
|
||||
// This can throw
|
||||
var_proxy_is_array.Bind(
|
||||
CallRuntime(Runtime::kArrayIsArray, context, receiver));
|
||||
Goto(&checkstringtag);
|
||||
}
|
||||
|
||||
Bind(&checkstringtag);
|
||||
{
|
||||
Node* to_string_tag_symbol =
|
||||
HeapConstant(isolate()->factory()->to_string_tag_symbol());
|
||||
|
||||
GetPropertyStub stub(isolate());
|
||||
Callable get_property =
|
||||
Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
|
||||
Node* to_string_tag_value =
|
||||
CallStub(get_property, context, receiver, to_string_tag_symbol);
|
||||
|
||||
IsString(to_string_tag_value, &if_tostringtag, &if_notostringtag);
|
||||
|
||||
Bind(&if_tostringtag);
|
||||
ReturnToStringFormat(context, to_string_tag_value);
|
||||
}
|
||||
Bind(&if_notostringtag);
|
||||
{
|
||||
size_t const kNumCases = 11;
|
||||
Label* case_labels[kNumCases];
|
||||
int32_t case_values[kNumCases];
|
||||
case_labels[0] = &return_api;
|
||||
case_values[0] = JS_API_OBJECT_TYPE;
|
||||
case_labels[1] = &return_api;
|
||||
case_values[1] = JS_SPECIAL_API_OBJECT_TYPE;
|
||||
case_labels[2] = &return_arguments;
|
||||
case_values[2] = JS_ARGUMENTS_TYPE;
|
||||
case_labels[3] = &return_array;
|
||||
case_values[3] = JS_ARRAY_TYPE;
|
||||
case_labels[4] = &return_function;
|
||||
case_values[4] = JS_BOUND_FUNCTION_TYPE;
|
||||
case_labels[5] = &return_function;
|
||||
case_values[5] = JS_FUNCTION_TYPE;
|
||||
case_labels[6] = &return_error;
|
||||
case_values[6] = JS_ERROR_TYPE;
|
||||
case_labels[7] = &return_date;
|
||||
case_values[7] = JS_DATE_TYPE;
|
||||
case_labels[8] = &return_regexp;
|
||||
case_values[8] = JS_REGEXP_TYPE;
|
||||
case_labels[9] = &return_jsvalue;
|
||||
case_values[9] = JS_VALUE_TYPE;
|
||||
case_labels[10] = &return_jsproxy;
|
||||
case_values[10] = JS_PROXY_TYPE;
|
||||
|
||||
Switch(receiver_instance_type, &return_object, case_values, case_labels,
|
||||
arraysize(case_values));
|
||||
|
||||
Bind(&return_undefined);
|
||||
Return(HeapConstant(isolate()->factory()->undefined_to_string()));
|
||||
|
||||
Bind(&return_null);
|
||||
Return(HeapConstant(isolate()->factory()->null_to_string()));
|
||||
|
||||
Bind(&return_arguments);
|
||||
Return(HeapConstant(isolate()->factory()->arguments_to_string()));
|
||||
|
||||
Bind(&return_array);
|
||||
Return(HeapConstant(isolate()->factory()->array_to_string()));
|
||||
|
||||
Bind(&return_function);
|
||||
Return(HeapConstant(isolate()->factory()->function_to_string()));
|
||||
|
||||
Bind(&return_error);
|
||||
Return(HeapConstant(isolate()->factory()->error_to_string()));
|
||||
|
||||
Bind(&return_date);
|
||||
Return(HeapConstant(isolate()->factory()->date_to_string()));
|
||||
|
||||
Bind(&return_regexp);
|
||||
Return(HeapConstant(isolate()->factory()->regexp_to_string()));
|
||||
|
||||
Bind(&return_api);
|
||||
{
|
||||
Node* class_name = CallRuntime(Runtime::kClassOf, context, receiver);
|
||||
ReturnToStringFormat(context, class_name);
|
||||
}
|
||||
|
||||
Bind(&return_jsvalue);
|
||||
{
|
||||
Label return_boolean(this), return_number(this), return_string(this);
|
||||
|
||||
Node* value = LoadJSValueValue(receiver);
|
||||
GotoIf(TaggedIsSmi(value), &return_number);
|
||||
Node* instance_type = LoadInstanceType(value);
|
||||
|
||||
GotoIf(IsStringInstanceType(instance_type), &return_string);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(HEAP_NUMBER_TYPE)),
|
||||
&return_number);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)),
|
||||
&return_boolean);
|
||||
|
||||
CSA_ASSERT(this, Word32Equal(instance_type, Int32Constant(SYMBOL_TYPE)));
|
||||
Goto(&return_object);
|
||||
|
||||
Bind(&return_string);
|
||||
Return(HeapConstant(isolate()->factory()->string_to_string()));
|
||||
|
||||
Bind(&return_number);
|
||||
Return(HeapConstant(isolate()->factory()->number_to_string()));
|
||||
|
||||
Bind(&return_boolean);
|
||||
Return(HeapConstant(isolate()->factory()->boolean_to_string()));
|
||||
}
|
||||
|
||||
Bind(&return_jsproxy);
|
||||
{
|
||||
GotoIf(WordEqual(var_proxy_is_array.value(), BooleanConstant(true)),
|
||||
&return_array);
|
||||
|
||||
Node* map = LoadMap(receiver);
|
||||
|
||||
// Return object if the proxy {receiver} is not callable.
|
||||
Branch(IsCallableMap(map), &return_function, &return_object);
|
||||
}
|
||||
|
||||
// Default
|
||||
Bind(&return_object);
|
||||
Return(HeapConstant(isolate()->factory()->object_to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 19.3.7 Object.prototype.valueOf
|
||||
TF_BUILTIN(ObjectPrototypeValueOf, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Callable to_object = CodeFactory::ToObject(isolate());
|
||||
receiver = CallStub(to_object, context, receiver);
|
||||
|
||||
Return(receiver);
|
||||
}
|
||||
|
||||
TF_BUILTIN(ObjectCreate, ObjectBuiltinsAssembler) {
|
||||
Node* prototype = Parameter(1);
|
||||
Node* properties = Parameter(2);
|
||||
Node* context = Parameter(3 + 2);
|
||||
|
||||
Label call_runtime(this, Label::kDeferred), prototype_valid(this),
|
||||
no_properties(this);
|
||||
{
|
||||
Comment("Argument 1 check: prototype");
|
||||
GotoIf(WordEqual(prototype, NullConstant()), &prototype_valid);
|
||||
BranchIfJSReceiver(prototype, &prototype_valid, &call_runtime);
|
||||
}
|
||||
|
||||
Bind(&prototype_valid);
|
||||
{
|
||||
Comment("Argument 2 check: properties");
|
||||
// Check that we have a simple object
|
||||
GotoIf(TaggedIsSmi(properties), &call_runtime);
|
||||
// Undefined implies no properties.
|
||||
GotoIf(WordEqual(properties, UndefinedConstant()), &no_properties);
|
||||
Node* properties_map = LoadMap(properties);
|
||||
GotoIf(IsSpecialReceiverMap(properties_map), &call_runtime);
|
||||
// Stay on the fast path only if there are no elements.
|
||||
GotoIfNot(WordEqual(LoadElements(properties),
|
||||
LoadRoot(Heap::kEmptyFixedArrayRootIndex)),
|
||||
&call_runtime);
|
||||
// Handle dictionary objects or fast objects with properties in runtime.
|
||||
Node* bit_field3 = LoadMapBitField3(properties_map);
|
||||
GotoIf(IsSetWord32<Map::DictionaryMap>(bit_field3), &call_runtime);
|
||||
Branch(IsSetWord32<Map::NumberOfOwnDescriptorsBits>(bit_field3),
|
||||
&call_runtime, &no_properties);
|
||||
}
|
||||
|
||||
// Create a new object with the given prototype.
|
||||
Bind(&no_properties);
|
||||
{
|
||||
Variable map(this, MachineRepresentation::kTagged);
|
||||
Variable properties(this, MachineRepresentation::kTagged);
|
||||
Label non_null_proto(this), instantiate_map(this), good(this);
|
||||
|
||||
Branch(WordEqual(prototype, NullConstant()), &good, &non_null_proto);
|
||||
|
||||
Bind(&good);
|
||||
{
|
||||
map.Bind(LoadContextElement(
|
||||
context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
|
||||
properties.Bind(AllocateNameDictionary(NameDictionary::kInitialCapacity));
|
||||
Goto(&instantiate_map);
|
||||
}
|
||||
|
||||
Bind(&non_null_proto);
|
||||
{
|
||||
properties.Bind(EmptyFixedArrayConstant());
|
||||
Node* object_function =
|
||||
LoadContextElement(context, Context::OBJECT_FUNCTION_INDEX);
|
||||
Node* object_function_map = LoadObjectField(
|
||||
object_function, JSFunction::kPrototypeOrInitialMapOffset);
|
||||
map.Bind(object_function_map);
|
||||
GotoIf(WordEqual(prototype, LoadMapPrototype(map.value())),
|
||||
&instantiate_map);
|
||||
// Try loading the prototype info.
|
||||
Node* prototype_info =
|
||||
LoadMapPrototypeInfo(LoadMap(prototype), &call_runtime);
|
||||
Comment("Load ObjectCreateMap from PrototypeInfo");
|
||||
Node* weak_cell =
|
||||
LoadObjectField(prototype_info, PrototypeInfo::kObjectCreateMap);
|
||||
GotoIf(WordEqual(weak_cell, UndefinedConstant()), &call_runtime);
|
||||
map.Bind(LoadWeakCellValue(weak_cell, &call_runtime));
|
||||
Goto(&instantiate_map);
|
||||
}
|
||||
|
||||
Bind(&instantiate_map);
|
||||
{
|
||||
Node* instance = AllocateJSObjectFromMap(map.value(), properties.value());
|
||||
Return(instance);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&call_runtime);
|
||||
{
|
||||
Return(CallRuntime(Runtime::kObjectCreate, context, prototype, properties));
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(CreateIterResultObject, ObjectBuiltinsAssembler) {
|
||||
typedef CreateIterResultObjectDescriptor Descriptor;
|
||||
|
||||
Node* const value = Parameter(Descriptor::kValue);
|
||||
Node* const done = Parameter(Descriptor::kDone);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const map =
|
||||
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
|
||||
|
||||
Node* const result = AllocateJSObjectFromMap(map);
|
||||
|
||||
StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, value);
|
||||
StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, done);
|
||||
|
||||
Return(result);
|
||||
}
|
||||
|
||||
TF_BUILTIN(HasProperty, ObjectBuiltinsAssembler) {
|
||||
typedef HasPropertyDescriptor Descriptor;
|
||||
|
||||
Node* key = Parameter(Descriptor::kKey);
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(HasProperty(object, key, context, Runtime::kHasProperty));
|
||||
}
|
||||
|
||||
TF_BUILTIN(InstanceOf, ObjectBuiltinsAssembler) {
|
||||
typedef CompareDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kLeft);
|
||||
Node* callable = Parameter(Descriptor::kRight);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(InstanceOf(object, callable, context));
|
||||
}
|
||||
|
||||
// ES6 section 7.3.19 OrdinaryHasInstance ( C, O )
|
||||
TF_BUILTIN(OrdinaryHasInstance, ObjectBuiltinsAssembler) {
|
||||
typedef CompareDescriptor Descriptor;
|
||||
|
||||
Node* constructor = Parameter(Descriptor::kLeft);
|
||||
Node* object = Parameter(Descriptor::kRight);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(OrdinaryHasInstance(context, constructor, object));
|
||||
}
|
||||
|
||||
TF_BUILTIN(GetSuperConstructor, ObjectBuiltinsAssembler) {
|
||||
typedef TypeofDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(GetSuperConstructor(object, context));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -15,89 +15,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
typedef compiler::Node Node;
|
||||
|
||||
class ObjectBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit ObjectBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void IsString(Node* object, Label* if_string, Label* if_notstring);
|
||||
void ReturnToStringFormat(Node* context, Node* string);
|
||||
};
|
||||
|
||||
void ObjectBuiltinsAssembler::IsString(Node* object, Label* if_string,
|
||||
Label* if_notstring) {
|
||||
Label if_notsmi(this);
|
||||
Branch(TaggedIsSmi(object), if_notstring, &if_notsmi);
|
||||
|
||||
Bind(&if_notsmi);
|
||||
{
|
||||
Node* instance_type = LoadInstanceType(object);
|
||||
|
||||
Branch(IsStringInstanceType(instance_type), if_string, if_notstring);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectBuiltinsAssembler::ReturnToStringFormat(Node* context,
|
||||
Node* string) {
|
||||
Node* lhs = HeapConstant(factory()->NewStringFromStaticChars("[object "));
|
||||
Node* rhs = HeapConstant(factory()->NewStringFromStaticChars("]"));
|
||||
|
||||
Callable callable =
|
||||
CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
|
||||
|
||||
Return(CallStub(callable, context, CallStub(callable, context, lhs, string),
|
||||
rhs));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 19.1 Object Objects
|
||||
|
||||
TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) {
|
||||
Node* object = Parameter(0);
|
||||
Node* key = Parameter(1);
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Label call_runtime(this), return_true(this), return_false(this);
|
||||
|
||||
// Smi receivers do not have own properties.
|
||||
Label if_objectisnotsmi(this);
|
||||
Branch(TaggedIsSmi(object), &return_false, &if_objectisnotsmi);
|
||||
Bind(&if_objectisnotsmi);
|
||||
|
||||
Node* map = LoadMap(object);
|
||||
Node* instance_type = LoadMapInstanceType(map);
|
||||
|
||||
{
|
||||
Variable var_index(this, MachineType::PointerRepresentation());
|
||||
Variable var_unique(this, MachineRepresentation::kTagged);
|
||||
|
||||
Label keyisindex(this), if_iskeyunique(this);
|
||||
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &var_unique,
|
||||
&call_runtime);
|
||||
|
||||
Bind(&if_iskeyunique);
|
||||
TryHasOwnProperty(object, map, instance_type, var_unique.value(),
|
||||
&return_true, &return_false, &call_runtime);
|
||||
|
||||
Bind(&keyisindex);
|
||||
// Handle negative keys in the runtime.
|
||||
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)), &call_runtime);
|
||||
TryLookupElement(object, map, instance_type, var_index.value(),
|
||||
&return_true, &return_false, &call_runtime);
|
||||
}
|
||||
Bind(&return_true);
|
||||
Return(BooleanConstant(true));
|
||||
|
||||
Bind(&return_false);
|
||||
Return(BooleanConstant(false));
|
||||
|
||||
Bind(&call_runtime);
|
||||
Return(CallRuntime(Runtime::kObjectHasOwnProperty, context, object, key));
|
||||
}
|
||||
|
||||
// ES6 19.1.2.1 Object.assign
|
||||
BUILTIN(ObjectAssign) {
|
||||
HandleScope scope(isolate);
|
||||
@ -137,264 +57,6 @@ BUILTIN(ObjectPrototypePropertyIsEnumerable) {
|
||||
return isolate->heap()->ToBoolean((maybe.FromJust() & DONT_ENUM) == 0);
|
||||
}
|
||||
|
||||
// ES6 section 19.1.3.6 Object.prototype.toString
|
||||
TF_BUILTIN(ObjectProtoToString, ObjectBuiltinsAssembler) {
|
||||
Label return_undefined(this, Label::kDeferred),
|
||||
return_null(this, Label::kDeferred),
|
||||
return_arguments(this, Label::kDeferred), return_array(this),
|
||||
return_api(this, Label::kDeferred), return_object(this),
|
||||
return_regexp(this), return_function(this), return_error(this),
|
||||
return_date(this), return_jsvalue(this),
|
||||
return_jsproxy(this, Label::kDeferred);
|
||||
|
||||
Label if_isproxy(this, Label::kDeferred);
|
||||
|
||||
Label checkstringtag(this);
|
||||
Label if_tostringtag(this), if_notostringtag(this);
|
||||
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
GotoIf(WordEqual(receiver, UndefinedConstant()), &return_undefined);
|
||||
|
||||
GotoIf(WordEqual(receiver, NullConstant()), &return_null);
|
||||
|
||||
Callable to_object = CodeFactory::ToObject(isolate());
|
||||
receiver = CallStub(to_object, context, receiver);
|
||||
|
||||
Node* receiver_instance_type = LoadInstanceType(receiver);
|
||||
|
||||
// for proxies, check IsArray before getting @@toStringTag
|
||||
Variable var_proxy_is_array(this, MachineRepresentation::kTagged);
|
||||
var_proxy_is_array.Bind(BooleanConstant(false));
|
||||
|
||||
Branch(Word32Equal(receiver_instance_type, Int32Constant(JS_PROXY_TYPE)),
|
||||
&if_isproxy, &checkstringtag);
|
||||
|
||||
Bind(&if_isproxy);
|
||||
{
|
||||
// This can throw
|
||||
var_proxy_is_array.Bind(
|
||||
CallRuntime(Runtime::kArrayIsArray, context, receiver));
|
||||
Goto(&checkstringtag);
|
||||
}
|
||||
|
||||
Bind(&checkstringtag);
|
||||
{
|
||||
Node* to_string_tag_symbol =
|
||||
HeapConstant(isolate()->factory()->to_string_tag_symbol());
|
||||
|
||||
GetPropertyStub stub(isolate());
|
||||
Callable get_property =
|
||||
Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
|
||||
Node* to_string_tag_value =
|
||||
CallStub(get_property, context, receiver, to_string_tag_symbol);
|
||||
|
||||
IsString(to_string_tag_value, &if_tostringtag, &if_notostringtag);
|
||||
|
||||
Bind(&if_tostringtag);
|
||||
ReturnToStringFormat(context, to_string_tag_value);
|
||||
}
|
||||
Bind(&if_notostringtag);
|
||||
{
|
||||
size_t const kNumCases = 11;
|
||||
Label* case_labels[kNumCases];
|
||||
int32_t case_values[kNumCases];
|
||||
case_labels[0] = &return_api;
|
||||
case_values[0] = JS_API_OBJECT_TYPE;
|
||||
case_labels[1] = &return_api;
|
||||
case_values[1] = JS_SPECIAL_API_OBJECT_TYPE;
|
||||
case_labels[2] = &return_arguments;
|
||||
case_values[2] = JS_ARGUMENTS_TYPE;
|
||||
case_labels[3] = &return_array;
|
||||
case_values[3] = JS_ARRAY_TYPE;
|
||||
case_labels[4] = &return_function;
|
||||
case_values[4] = JS_BOUND_FUNCTION_TYPE;
|
||||
case_labels[5] = &return_function;
|
||||
case_values[5] = JS_FUNCTION_TYPE;
|
||||
case_labels[6] = &return_error;
|
||||
case_values[6] = JS_ERROR_TYPE;
|
||||
case_labels[7] = &return_date;
|
||||
case_values[7] = JS_DATE_TYPE;
|
||||
case_labels[8] = &return_regexp;
|
||||
case_values[8] = JS_REGEXP_TYPE;
|
||||
case_labels[9] = &return_jsvalue;
|
||||
case_values[9] = JS_VALUE_TYPE;
|
||||
case_labels[10] = &return_jsproxy;
|
||||
case_values[10] = JS_PROXY_TYPE;
|
||||
|
||||
Switch(receiver_instance_type, &return_object, case_values, case_labels,
|
||||
arraysize(case_values));
|
||||
|
||||
Bind(&return_undefined);
|
||||
Return(HeapConstant(isolate()->factory()->undefined_to_string()));
|
||||
|
||||
Bind(&return_null);
|
||||
Return(HeapConstant(isolate()->factory()->null_to_string()));
|
||||
|
||||
Bind(&return_arguments);
|
||||
Return(HeapConstant(isolate()->factory()->arguments_to_string()));
|
||||
|
||||
Bind(&return_array);
|
||||
Return(HeapConstant(isolate()->factory()->array_to_string()));
|
||||
|
||||
Bind(&return_function);
|
||||
Return(HeapConstant(isolate()->factory()->function_to_string()));
|
||||
|
||||
Bind(&return_error);
|
||||
Return(HeapConstant(isolate()->factory()->error_to_string()));
|
||||
|
||||
Bind(&return_date);
|
||||
Return(HeapConstant(isolate()->factory()->date_to_string()));
|
||||
|
||||
Bind(&return_regexp);
|
||||
Return(HeapConstant(isolate()->factory()->regexp_to_string()));
|
||||
|
||||
Bind(&return_api);
|
||||
{
|
||||
Node* class_name = CallRuntime(Runtime::kClassOf, context, receiver);
|
||||
ReturnToStringFormat(context, class_name);
|
||||
}
|
||||
|
||||
Bind(&return_jsvalue);
|
||||
{
|
||||
Label return_boolean(this), return_number(this), return_string(this);
|
||||
|
||||
Node* value = LoadJSValueValue(receiver);
|
||||
GotoIf(TaggedIsSmi(value), &return_number);
|
||||
Node* instance_type = LoadInstanceType(value);
|
||||
|
||||
GotoIf(IsStringInstanceType(instance_type), &return_string);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(HEAP_NUMBER_TYPE)),
|
||||
&return_number);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)),
|
||||
&return_boolean);
|
||||
|
||||
CSA_ASSERT(this, Word32Equal(instance_type, Int32Constant(SYMBOL_TYPE)));
|
||||
Goto(&return_object);
|
||||
|
||||
Bind(&return_string);
|
||||
Return(HeapConstant(isolate()->factory()->string_to_string()));
|
||||
|
||||
Bind(&return_number);
|
||||
Return(HeapConstant(isolate()->factory()->number_to_string()));
|
||||
|
||||
Bind(&return_boolean);
|
||||
Return(HeapConstant(isolate()->factory()->boolean_to_string()));
|
||||
}
|
||||
|
||||
Bind(&return_jsproxy);
|
||||
{
|
||||
GotoIf(WordEqual(var_proxy_is_array.value(), BooleanConstant(true)),
|
||||
&return_array);
|
||||
|
||||
Node* map = LoadMap(receiver);
|
||||
|
||||
// Return object if the proxy {receiver} is not callable.
|
||||
Branch(IsCallableMap(map), &return_function, &return_object);
|
||||
}
|
||||
|
||||
// Default
|
||||
Bind(&return_object);
|
||||
Return(HeapConstant(isolate()->factory()->object_to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 19.3.7 Object.prototype.valueOf
|
||||
TF_BUILTIN(ObjectPrototypeValueOf, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Callable to_object = CodeFactory::ToObject(isolate());
|
||||
receiver = CallStub(to_object, context, receiver);
|
||||
|
||||
Return(receiver);
|
||||
}
|
||||
|
||||
TF_BUILTIN(ObjectCreate, ObjectBuiltinsAssembler) {
|
||||
Node* prototype = Parameter(1);
|
||||
Node* properties = Parameter(2);
|
||||
Node* context = Parameter(3 + 2);
|
||||
|
||||
Label call_runtime(this, Label::kDeferred), prototype_valid(this),
|
||||
no_properties(this);
|
||||
{
|
||||
Comment("Argument 1 check: prototype");
|
||||
GotoIf(WordEqual(prototype, NullConstant()), &prototype_valid);
|
||||
BranchIfJSReceiver(prototype, &prototype_valid, &call_runtime);
|
||||
}
|
||||
|
||||
Bind(&prototype_valid);
|
||||
{
|
||||
Comment("Argument 2 check: properties");
|
||||
// Check that we have a simple object
|
||||
GotoIf(TaggedIsSmi(properties), &call_runtime);
|
||||
// Undefined implies no properties.
|
||||
GotoIf(WordEqual(properties, UndefinedConstant()), &no_properties);
|
||||
Node* properties_map = LoadMap(properties);
|
||||
GotoIf(IsSpecialReceiverMap(properties_map), &call_runtime);
|
||||
// Stay on the fast path only if there are no elements.
|
||||
GotoIfNot(WordEqual(LoadElements(properties),
|
||||
LoadRoot(Heap::kEmptyFixedArrayRootIndex)),
|
||||
&call_runtime);
|
||||
// Handle dictionary objects or fast objects with properties in runtime.
|
||||
Node* bit_field3 = LoadMapBitField3(properties_map);
|
||||
GotoIf(IsSetWord32<Map::DictionaryMap>(bit_field3), &call_runtime);
|
||||
Branch(IsSetWord32<Map::NumberOfOwnDescriptorsBits>(bit_field3),
|
||||
&call_runtime, &no_properties);
|
||||
}
|
||||
|
||||
// Create a new object with the given prototype.
|
||||
Bind(&no_properties);
|
||||
{
|
||||
Variable map(this, MachineRepresentation::kTagged);
|
||||
Variable properties(this, MachineRepresentation::kTagged);
|
||||
Label non_null_proto(this), instantiate_map(this), good(this);
|
||||
|
||||
Branch(WordEqual(prototype, NullConstant()), &good, &non_null_proto);
|
||||
|
||||
Bind(&good);
|
||||
{
|
||||
map.Bind(LoadContextElement(
|
||||
context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
|
||||
properties.Bind(AllocateNameDictionary(NameDictionary::kInitialCapacity));
|
||||
Goto(&instantiate_map);
|
||||
}
|
||||
|
||||
Bind(&non_null_proto);
|
||||
{
|
||||
properties.Bind(EmptyFixedArrayConstant());
|
||||
Node* object_function =
|
||||
LoadContextElement(context, Context::OBJECT_FUNCTION_INDEX);
|
||||
Node* object_function_map = LoadObjectField(
|
||||
object_function, JSFunction::kPrototypeOrInitialMapOffset);
|
||||
map.Bind(object_function_map);
|
||||
GotoIf(WordEqual(prototype, LoadMapPrototype(map.value())),
|
||||
&instantiate_map);
|
||||
// Try loading the prototype info.
|
||||
Node* prototype_info =
|
||||
LoadMapPrototypeInfo(LoadMap(prototype), &call_runtime);
|
||||
Comment("Load ObjectCreateMap from PrototypeInfo");
|
||||
Node* weak_cell =
|
||||
LoadObjectField(prototype_info, PrototypeInfo::kObjectCreateMap);
|
||||
GotoIf(WordEqual(weak_cell, UndefinedConstant()), &call_runtime);
|
||||
map.Bind(LoadWeakCellValue(weak_cell, &call_runtime));
|
||||
Goto(&instantiate_map);
|
||||
}
|
||||
|
||||
Bind(&instantiate_map);
|
||||
{
|
||||
Node* instance = AllocateJSObjectFromMap(map.value(), properties.value());
|
||||
Return(instance);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&call_runtime);
|
||||
{
|
||||
Return(CallRuntime(Runtime::kObjectCreate, context, prototype, properties));
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 19.1.2.3 Object.defineProperties
|
||||
BUILTIN(ObjectDefineProperties) {
|
||||
HandleScope scope(isolate);
|
||||
@ -888,64 +550,5 @@ BUILTIN(ObjectSeal) {
|
||||
return *object;
|
||||
}
|
||||
|
||||
TF_BUILTIN(CreateIterResultObject, ObjectBuiltinsAssembler) {
|
||||
typedef CreateIterResultObjectDescriptor Descriptor;
|
||||
|
||||
Node* const value = Parameter(Descriptor::kValue);
|
||||
Node* const done = Parameter(Descriptor::kDone);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const map =
|
||||
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
|
||||
|
||||
Node* const result = AllocateJSObjectFromMap(map);
|
||||
|
||||
StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, value);
|
||||
StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, done);
|
||||
|
||||
Return(result);
|
||||
}
|
||||
|
||||
TF_BUILTIN(HasProperty, ObjectBuiltinsAssembler) {
|
||||
typedef HasPropertyDescriptor Descriptor;
|
||||
|
||||
Node* key = Parameter(Descriptor::kKey);
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(HasProperty(object, key, context, Runtime::kHasProperty));
|
||||
}
|
||||
|
||||
TF_BUILTIN(InstanceOf, ObjectBuiltinsAssembler) {
|
||||
typedef CompareDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kLeft);
|
||||
Node* callable = Parameter(Descriptor::kRight);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(InstanceOf(object, callable, context));
|
||||
}
|
||||
|
||||
// ES6 section 7.3.19 OrdinaryHasInstance ( C, O )
|
||||
TF_BUILTIN(OrdinaryHasInstance, ObjectBuiltinsAssembler) {
|
||||
typedef CompareDescriptor Descriptor;
|
||||
|
||||
Node* constructor = Parameter(Descriptor::kLeft);
|
||||
Node* object = Parameter(Descriptor::kRight);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(OrdinaryHasInstance(context, constructor, object));
|
||||
}
|
||||
|
||||
TF_BUILTIN(GetSuperConstructor, ObjectBuiltinsAssembler) {
|
||||
typedef TypeofDescriptor Descriptor;
|
||||
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(GetSuperConstructor(object, context));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -2,9 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-promise.h"
|
||||
#include "src/builtins/builtins-constructor.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-promise.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
@ -13,6 +13,8 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
|
||||
Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) {
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const promise_fun =
|
||||
@ -410,7 +412,6 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
|
||||
Node* context, Node* promise, Node* on_resolve, Node* on_reject,
|
||||
Node* deferred_promise, Node* deferred_on_resolve,
|
||||
Node* deferred_on_reject) {
|
||||
|
||||
Variable var_on_resolve(this, MachineRepresentation::kTagged),
|
||||
var_on_reject(this, MachineRepresentation::kTagged);
|
||||
|
@ -11,8 +11,6 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
typedef compiler::Node Node;
|
||||
typedef CodeStubAssembler::ParameterMode ParameterMode;
|
||||
typedef compiler::CodeAssemblerState CodeAssemblerState;
|
||||
|
||||
class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
@ -48,7 +46,7 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
kOnFinallyContextLength,
|
||||
};
|
||||
|
||||
explicit PromiseBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit PromiseBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
// These allocate and initialize a promise with pending state and
|
||||
// undefined fields.
|
||||
|
2687
src/builtins/builtins-regexp-gen.cc
Normal file
2687
src/builtins/builtins-regexp-gen.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,13 +10,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
typedef compiler::Node Node;
|
||||
typedef compiler::CodeAssemblerState CodeAssemblerState;
|
||||
typedef compiler::CodeAssemblerLabel CodeAssemblerLabel;
|
||||
|
||||
class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit RegExpBuiltinsAssembler(CodeAssemblerState* state)
|
||||
explicit RegExpBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
void BranchIfFastRegExp(Node* const context, Node* const map,
|
File diff suppressed because it is too large
Load Diff
326
src/builtins/builtins-sharedarraybuffer-gen.cc
Normal file
326
src/builtins/builtins-sharedarraybuffer-gen.cc
Normal file
@ -0,0 +1,326 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/objects.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
|
||||
class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit SharedArrayBufferBuiltinsAssembler(
|
||||
compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void ValidateSharedTypedArray(Node* tagged, Node* context,
|
||||
Node** out_instance_type,
|
||||
Node** out_backing_store);
|
||||
Node* ConvertTaggedAtomicIndexToWord32(Node* tagged, Node* context,
|
||||
Node** number_index);
|
||||
void ValidateAtomicIndex(Node* index_word, Node* array_length_word,
|
||||
Node* context);
|
||||
};
|
||||
|
||||
void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
|
||||
Node* tagged, Node* context, Node** out_instance_type,
|
||||
Node** out_backing_store) {
|
||||
Label not_float_or_clamped(this), invalid(this);
|
||||
|
||||
// Fail if it is not a heap object.
|
||||
GotoIf(TaggedIsSmi(tagged), &invalid);
|
||||
|
||||
// Fail if the array's instance type is not JSTypedArray.
|
||||
GotoIf(Word32NotEqual(LoadInstanceType(tagged),
|
||||
Int32Constant(JS_TYPED_ARRAY_TYPE)),
|
||||
&invalid);
|
||||
|
||||
// Fail if the array's JSArrayBuffer is not shared.
|
||||
Node* array_buffer = LoadObjectField(tagged, JSTypedArray::kBufferOffset);
|
||||
Node* bitfield = LoadObjectField(array_buffer, JSArrayBuffer::kBitFieldOffset,
|
||||
MachineType::Uint32());
|
||||
GotoIfNot(IsSetWord32<JSArrayBuffer::IsShared>(bitfield), &invalid);
|
||||
|
||||
// Fail if the array's element type is float32, float64 or clamped.
|
||||
Node* elements_instance_type =
|
||||
LoadInstanceType(LoadObjectField(tagged, JSObject::kElementsOffset));
|
||||
STATIC_ASSERT(FIXED_INT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_INT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_INT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
Branch(Int32LessThan(elements_instance_type,
|
||||
Int32Constant(FIXED_FLOAT32_ARRAY_TYPE)),
|
||||
¬_float_or_clamped, &invalid);
|
||||
|
||||
Bind(&invalid);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowNotIntegerSharedTypedArrayError, context,
|
||||
tagged);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(¬_float_or_clamped);
|
||||
*out_instance_type = elements_instance_type;
|
||||
|
||||
Node* backing_store =
|
||||
LoadObjectField(array_buffer, JSArrayBuffer::kBackingStoreOffset);
|
||||
Node* byte_offset = ChangeUint32ToWord(TruncateTaggedToWord32(
|
||||
context, LoadObjectField(tagged, JSArrayBufferView::kByteOffsetOffset)));
|
||||
*out_backing_store =
|
||||
IntPtrAdd(BitcastTaggedToWord(backing_store), byte_offset);
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomicAccess
|
||||
Node* SharedArrayBufferBuiltinsAssembler::ConvertTaggedAtomicIndexToWord32(
|
||||
Node* tagged, Node* context, Node** number_index) {
|
||||
Variable var_result(this, MachineRepresentation::kWord32);
|
||||
|
||||
// TODO(jkummerow): Skip ToNumber call when |tagged| is a number already.
|
||||
// Maybe this can be unified with other tagged-to-index conversions?
|
||||
// Why does this return an int32, and not an intptr?
|
||||
// Why is there the additional |number_index| output parameter?
|
||||
Callable to_number = CodeFactory::ToNumber(isolate());
|
||||
*number_index = CallStub(to_number, context, tagged);
|
||||
Label done(this, &var_result);
|
||||
|
||||
Label if_numberissmi(this), if_numberisnotsmi(this);
|
||||
Branch(TaggedIsSmi(*number_index), &if_numberissmi, &if_numberisnotsmi);
|
||||
|
||||
Bind(&if_numberissmi);
|
||||
{
|
||||
var_result.Bind(SmiToWord32(*number_index));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&if_numberisnotsmi);
|
||||
{
|
||||
Node* number_index_value = LoadHeapNumberValue(*number_index);
|
||||
Node* access_index = TruncateFloat64ToWord32(number_index_value);
|
||||
Node* test_index = ChangeInt32ToFloat64(access_index);
|
||||
|
||||
Label if_indexesareequal(this), if_indexesarenotequal(this);
|
||||
Branch(Float64Equal(number_index_value, test_index), &if_indexesareequal,
|
||||
&if_indexesarenotequal);
|
||||
|
||||
Bind(&if_indexesareequal);
|
||||
{
|
||||
var_result.Bind(access_index);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&if_indexesarenotequal);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void SharedArrayBufferBuiltinsAssembler::ValidateAtomicIndex(
|
||||
Node* index_word, Node* array_length_word, Node* context) {
|
||||
// Check if the index is in bounds. If not, throw RangeError.
|
||||
Label check_passed(this);
|
||||
GotoIf(Uint32LessThan(index_word, array_length_word), &check_passed);
|
||||
|
||||
CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context);
|
||||
Unreachable();
|
||||
|
||||
Bind(&check_passed);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(1);
|
||||
Node* index = Parameter(2);
|
||||
Node* context = Parameter(3 + 2);
|
||||
|
||||
Node* instance_type;
|
||||
Node* backing_store;
|
||||
ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
|
||||
|
||||
Node* index_integer;
|
||||
Node* index_word32 =
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
Node* array_length_word32 = TruncateTaggedToWord32(
|
||||
context, LoadObjectField(array, JSTypedArray::kLengthOffset));
|
||||
ValidateAtomicIndex(index_word32, array_length_word32, context);
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&i8, &u8, &i16, &u16, &i32, &u32,
|
||||
};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
Bind(&i8);
|
||||
Return(SmiFromWord32(
|
||||
AtomicLoad(MachineType::Int8(), backing_store, index_word)));
|
||||
|
||||
Bind(&u8);
|
||||
Return(SmiFromWord32(
|
||||
AtomicLoad(MachineType::Uint8(), backing_store, index_word)));
|
||||
|
||||
Bind(&i16);
|
||||
Return(SmiFromWord32(
|
||||
AtomicLoad(MachineType::Int16(), backing_store, WordShl(index_word, 1))));
|
||||
|
||||
Bind(&u16);
|
||||
Return(SmiFromWord32(AtomicLoad(MachineType::Uint16(), backing_store,
|
||||
WordShl(index_word, 1))));
|
||||
|
||||
Bind(&i32);
|
||||
Return(ChangeInt32ToTagged(
|
||||
AtomicLoad(MachineType::Int32(), backing_store, WordShl(index_word, 2))));
|
||||
|
||||
Bind(&u32);
|
||||
Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2))));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
Bind(&other);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(1);
|
||||
Node* index = Parameter(2);
|
||||
Node* value = Parameter(3);
|
||||
Node* context = Parameter(4 + 2);
|
||||
|
||||
Node* instance_type;
|
||||
Node* backing_store;
|
||||
ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
|
||||
|
||||
Node* index_integer;
|
||||
Node* index_word32 =
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
Node* array_length_word32 = TruncateTaggedToWord32(
|
||||
context, LoadObjectField(array, JSTypedArray::kLengthOffset));
|
||||
ValidateAtomicIndex(index_word32, array_length_word32, context);
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Node* value_integer = ToInteger(context, value);
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
Label u8(this), u16(this), u32(this), other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&u8, &u8, &u16, &u16, &u32, &u32,
|
||||
};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
Bind(&u8);
|
||||
AtomicStore(MachineRepresentation::kWord8, backing_store, index_word,
|
||||
value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
Bind(&u16);
|
||||
AtomicStore(MachineRepresentation::kWord16, backing_store,
|
||||
WordShl(index_word, 1), value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
Bind(&u32);
|
||||
AtomicStore(MachineRepresentation::kWord32, backing_store,
|
||||
WordShl(index_word, 2), value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
Bind(&other);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(1);
|
||||
Node* index = Parameter(2);
|
||||
Node* value = Parameter(3);
|
||||
Node* context = Parameter(4 + 2);
|
||||
|
||||
Node* instance_type;
|
||||
Node* backing_store;
|
||||
ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
|
||||
|
||||
Node* index_integer;
|
||||
Node* index_word32 =
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
Node* array_length_word32 = TruncateTaggedToWord32(
|
||||
context, LoadObjectField(array, JSTypedArray::kLengthOffset));
|
||||
ValidateAtomicIndex(index_word32, array_length_word32, context);
|
||||
|
||||
Node* value_integer = ToInteger(context, value);
|
||||
|
||||
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
|
||||
V8_TARGET_ARCH_PPC
|
||||
Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer,
|
||||
value_integer));
|
||||
#else
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&i8, &u8, &i16, &u16, &i32, &u32,
|
||||
};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
Bind(&i8);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Int8(), backing_store,
|
||||
index_word, value_word32)));
|
||||
|
||||
Bind(&u8);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Uint8(), backing_store,
|
||||
index_word, value_word32)));
|
||||
|
||||
Bind(&i16);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Int16(), backing_store,
|
||||
WordShl(index_word, 1), value_word32)));
|
||||
|
||||
Bind(&u16);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Uint16(), backing_store,
|
||||
WordShl(index_word, 1), value_word32)));
|
||||
|
||||
Bind(&i32);
|
||||
Return(ChangeInt32ToTagged(AtomicExchange(MachineType::Int32(), backing_store,
|
||||
WordShl(index_word, 2),
|
||||
value_word32)));
|
||||
|
||||
Bind(&u32);
|
||||
Return(ChangeUint32ToTagged(
|
||||
AtomicExchange(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2), value_word32)));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
Bind(&other);
|
||||
Unreachable();
|
||||
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
|
||||
// || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -8,7 +8,6 @@
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/conversions-inl.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/factory.h"
|
||||
@ -19,24 +18,6 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
|
||||
class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit SharedArrayBufferBuiltinsAssembler(
|
||||
compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void ValidateSharedTypedArray(Node* tagged, Node* context,
|
||||
Node** out_instance_type,
|
||||
Node** out_backing_store);
|
||||
Node* ConvertTaggedAtomicIndexToWord32(Node* tagged, Node* context,
|
||||
Node** number_index);
|
||||
void ValidateAtomicIndex(Node* index_word, Node* array_length_word,
|
||||
Node* context);
|
||||
};
|
||||
|
||||
// ES7 sharedmem 6.3.4.1 get SharedArrayBuffer.prototype.byteLength
|
||||
BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
|
||||
HandleScope scope(isolate);
|
||||
@ -52,300 +33,6 @@ BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
|
||||
return array_buffer->byte_length();
|
||||
}
|
||||
|
||||
void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
|
||||
Node* tagged, Node* context, Node** out_instance_type,
|
||||
Node** out_backing_store) {
|
||||
Label not_float_or_clamped(this), invalid(this);
|
||||
|
||||
// Fail if it is not a heap object.
|
||||
GotoIf(TaggedIsSmi(tagged), &invalid);
|
||||
|
||||
// Fail if the array's instance type is not JSTypedArray.
|
||||
GotoIf(Word32NotEqual(LoadInstanceType(tagged),
|
||||
Int32Constant(JS_TYPED_ARRAY_TYPE)),
|
||||
&invalid);
|
||||
|
||||
// Fail if the array's JSArrayBuffer is not shared.
|
||||
Node* array_buffer = LoadObjectField(tagged, JSTypedArray::kBufferOffset);
|
||||
Node* bitfield = LoadObjectField(array_buffer, JSArrayBuffer::kBitFieldOffset,
|
||||
MachineType::Uint32());
|
||||
GotoIfNot(IsSetWord32<JSArrayBuffer::IsShared>(bitfield), &invalid);
|
||||
|
||||
// Fail if the array's element type is float32, float64 or clamped.
|
||||
Node* elements_instance_type =
|
||||
LoadInstanceType(LoadObjectField(tagged, JSObject::kElementsOffset));
|
||||
STATIC_ASSERT(FIXED_INT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_INT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_INT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
Branch(Int32LessThan(elements_instance_type,
|
||||
Int32Constant(FIXED_FLOAT32_ARRAY_TYPE)),
|
||||
¬_float_or_clamped, &invalid);
|
||||
|
||||
Bind(&invalid);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowNotIntegerSharedTypedArrayError, context,
|
||||
tagged);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(¬_float_or_clamped);
|
||||
*out_instance_type = elements_instance_type;
|
||||
|
||||
Node* backing_store =
|
||||
LoadObjectField(array_buffer, JSArrayBuffer::kBackingStoreOffset);
|
||||
Node* byte_offset = ChangeUint32ToWord(TruncateTaggedToWord32(
|
||||
context, LoadObjectField(tagged, JSArrayBufferView::kByteOffsetOffset)));
|
||||
*out_backing_store =
|
||||
IntPtrAdd(BitcastTaggedToWord(backing_store), byte_offset);
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomicAccess
|
||||
Node* SharedArrayBufferBuiltinsAssembler::ConvertTaggedAtomicIndexToWord32(
|
||||
Node* tagged, Node* context, Node** number_index) {
|
||||
Variable var_result(this, MachineRepresentation::kWord32);
|
||||
|
||||
// TODO(jkummerow): Skip ToNumber call when |tagged| is a number already.
|
||||
// Maybe this can be unified with other tagged-to-index conversions?
|
||||
// Why does this return an int32, and not an intptr?
|
||||
// Why is there the additional |number_index| output parameter?
|
||||
Callable to_number = CodeFactory::ToNumber(isolate());
|
||||
*number_index = CallStub(to_number, context, tagged);
|
||||
Label done(this, &var_result);
|
||||
|
||||
Label if_numberissmi(this), if_numberisnotsmi(this);
|
||||
Branch(TaggedIsSmi(*number_index), &if_numberissmi, &if_numberisnotsmi);
|
||||
|
||||
Bind(&if_numberissmi);
|
||||
{
|
||||
var_result.Bind(SmiToWord32(*number_index));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&if_numberisnotsmi);
|
||||
{
|
||||
Node* number_index_value = LoadHeapNumberValue(*number_index);
|
||||
Node* access_index = TruncateFloat64ToWord32(number_index_value);
|
||||
Node* test_index = ChangeInt32ToFloat64(access_index);
|
||||
|
||||
Label if_indexesareequal(this), if_indexesarenotequal(this);
|
||||
Branch(Float64Equal(number_index_value, test_index), &if_indexesareequal,
|
||||
&if_indexesarenotequal);
|
||||
|
||||
Bind(&if_indexesareequal);
|
||||
{
|
||||
var_result.Bind(access_index);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&if_indexesarenotequal);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void SharedArrayBufferBuiltinsAssembler::ValidateAtomicIndex(
|
||||
Node* index_word, Node* array_length_word, Node* context) {
|
||||
// Check if the index is in bounds. If not, throw RangeError.
|
||||
Label check_passed(this);
|
||||
GotoIf(Uint32LessThan(index_word, array_length_word), &check_passed);
|
||||
|
||||
CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context);
|
||||
Unreachable();
|
||||
|
||||
Bind(&check_passed);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(1);
|
||||
Node* index = Parameter(2);
|
||||
Node* context = Parameter(3 + 2);
|
||||
|
||||
Node* instance_type;
|
||||
Node* backing_store;
|
||||
ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
|
||||
|
||||
Node* index_integer;
|
||||
Node* index_word32 =
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
Node* array_length_word32 = TruncateTaggedToWord32(
|
||||
context, LoadObjectField(array, JSTypedArray::kLengthOffset));
|
||||
ValidateAtomicIndex(index_word32, array_length_word32, context);
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&i8, &u8, &i16, &u16, &i32, &u32,
|
||||
};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
Bind(&i8);
|
||||
Return(SmiFromWord32(
|
||||
AtomicLoad(MachineType::Int8(), backing_store, index_word)));
|
||||
|
||||
Bind(&u8);
|
||||
Return(SmiFromWord32(
|
||||
AtomicLoad(MachineType::Uint8(), backing_store, index_word)));
|
||||
|
||||
Bind(&i16);
|
||||
Return(SmiFromWord32(
|
||||
AtomicLoad(MachineType::Int16(), backing_store, WordShl(index_word, 1))));
|
||||
|
||||
Bind(&u16);
|
||||
Return(SmiFromWord32(AtomicLoad(MachineType::Uint16(), backing_store,
|
||||
WordShl(index_word, 1))));
|
||||
|
||||
Bind(&i32);
|
||||
Return(ChangeInt32ToTagged(
|
||||
AtomicLoad(MachineType::Int32(), backing_store, WordShl(index_word, 2))));
|
||||
|
||||
Bind(&u32);
|
||||
Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2))));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
Bind(&other);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(1);
|
||||
Node* index = Parameter(2);
|
||||
Node* value = Parameter(3);
|
||||
Node* context = Parameter(4 + 2);
|
||||
|
||||
Node* instance_type;
|
||||
Node* backing_store;
|
||||
ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
|
||||
|
||||
Node* index_integer;
|
||||
Node* index_word32 =
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
Node* array_length_word32 = TruncateTaggedToWord32(
|
||||
context, LoadObjectField(array, JSTypedArray::kLengthOffset));
|
||||
ValidateAtomicIndex(index_word32, array_length_word32, context);
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Node* value_integer = ToInteger(context, value);
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
Label u8(this), u16(this), u32(this), other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&u8, &u8, &u16, &u16, &u32, &u32,
|
||||
};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
Bind(&u8);
|
||||
AtomicStore(MachineRepresentation::kWord8, backing_store, index_word,
|
||||
value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
Bind(&u16);
|
||||
AtomicStore(MachineRepresentation::kWord16, backing_store,
|
||||
WordShl(index_word, 1), value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
Bind(&u32);
|
||||
AtomicStore(MachineRepresentation::kWord32, backing_store,
|
||||
WordShl(index_word, 2), value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
Bind(&other);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(1);
|
||||
Node* index = Parameter(2);
|
||||
Node* value = Parameter(3);
|
||||
Node* context = Parameter(4 + 2);
|
||||
|
||||
Node* instance_type;
|
||||
Node* backing_store;
|
||||
ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
|
||||
|
||||
Node* index_integer;
|
||||
Node* index_word32 =
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
Node* array_length_word32 = TruncateTaggedToWord32(
|
||||
context, LoadObjectField(array, JSTypedArray::kLengthOffset));
|
||||
ValidateAtomicIndex(index_word32, array_length_word32, context);
|
||||
|
||||
Node* value_integer = ToInteger(context, value);
|
||||
|
||||
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
|
||||
V8_TARGET_ARCH_PPC
|
||||
Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer,
|
||||
value_integer));
|
||||
#else
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&i8, &u8, &i16, &u16, &i32, &u32,
|
||||
};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
Bind(&i8);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Int8(), backing_store,
|
||||
index_word, value_word32)));
|
||||
|
||||
Bind(&u8);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Uint8(), backing_store,
|
||||
index_word, value_word32)));
|
||||
|
||||
Bind(&i16);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Int16(), backing_store,
|
||||
WordShl(index_word, 1), value_word32)));
|
||||
|
||||
Bind(&u16);
|
||||
Return(SmiFromWord32(AtomicExchange(MachineType::Uint16(), backing_store,
|
||||
WordShl(index_word, 1), value_word32)));
|
||||
|
||||
Bind(&i32);
|
||||
Return(ChangeInt32ToTagged(AtomicExchange(MachineType::Int32(), backing_store,
|
||||
WordShl(index_word, 2),
|
||||
value_word32)));
|
||||
|
||||
Bind(&u32);
|
||||
Return(ChangeUint32ToTagged(
|
||||
AtomicExchange(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2), value_word32)));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
Bind(&other);
|
||||
Unreachable();
|
||||
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
|
||||
// || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
|
||||
}
|
||||
|
||||
inline bool AtomicIsLockFree(uint32_t size) {
|
||||
return size == 1 || size == 2 || size == 4;
|
||||
}
|
||||
|
1615
src/builtins/builtins-string-gen.cc
Normal file
1615
src/builtins/builtins-string-gen.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
47
src/builtins/builtins-symbol-gen.cc
Normal file
47
src/builtins/builtins-symbol-gen.cc
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 19.4 Symbol Objects
|
||||
|
||||
// ES6 section 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
|
||||
TF_BUILTIN(SymbolPrototypeToPrimitive, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Node* result = ToThisValue(context, receiver, PrimitiveType::kSymbol,
|
||||
"Symbol.prototype [ @@toPrimitive ]");
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 19.4.3.2 Symbol.prototype.toString ( )
|
||||
TF_BUILTIN(SymbolPrototypeToString, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Node* value = ToThisValue(context, receiver, PrimitiveType::kSymbol,
|
||||
"Symbol.prototype.toString");
|
||||
Node* result = CallRuntime(Runtime::kSymbolDescriptiveString, context, value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 19.4.3.3 Symbol.prototype.valueOf ( )
|
||||
TF_BUILTIN(SymbolPrototypeValueOf, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Node* result = ToThisValue(context, receiver, PrimitiveType::kSymbol,
|
||||
"Symbol.prototype.valueOf");
|
||||
Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
@ -67,36 +66,5 @@ BUILTIN(SymbolKeyFor) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// ES6 section 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
|
||||
TF_BUILTIN(SymbolPrototypeToPrimitive, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(4);
|
||||
|
||||
Node* result = ToThisValue(context, receiver, PrimitiveType::kSymbol,
|
||||
"Symbol.prototype [ @@toPrimitive ]");
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 19.4.3.2 Symbol.prototype.toString ( )
|
||||
TF_BUILTIN(SymbolPrototypeToString, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Node* value = ToThisValue(context, receiver, PrimitiveType::kSymbol,
|
||||
"Symbol.prototype.toString");
|
||||
Node* result = CallRuntime(Runtime::kSymbolDescriptiveString, context, value);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
// ES6 section 19.4.3.3 Symbol.prototype.valueOf ( )
|
||||
TF_BUILTIN(SymbolPrototypeValueOf, CodeStubAssembler) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Node* result = ToThisValue(context, receiver, PrimitiveType::kSymbol,
|
||||
"Symbol.prototype.valueOf");
|
||||
Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
583
src/builtins/builtins-typedarray-gen.cc
Normal file
583
src/builtins/builtins-typedarray-gen.cc
Normal file
@ -0,0 +1,583 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 22.2 TypedArray Objects
|
||||
|
||||
class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void GenerateTypedArrayPrototypeGetter(const char* method_name,
|
||||
int object_offset);
|
||||
template <IterationKind kIterationKind>
|
||||
void GenerateTypedArrayPrototypeIterationMethod(const char* method_name);
|
||||
|
||||
void LoadMapAndElementsSize(Node* const array, Variable* typed_map,
|
||||
Variable* size);
|
||||
|
||||
void CalculateExternalPointer(Node* const backing_store,
|
||||
Node* const byte_offset,
|
||||
Variable* external_pointer);
|
||||
void DoInitialize(Node* const holder, Node* length, Node* const maybe_buffer,
|
||||
Node* const byte_offset, Node* byte_length,
|
||||
Node* const initialize, Node* const context);
|
||||
};
|
||||
|
||||
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* const array,
|
||||
Variable* typed_map,
|
||||
Variable* size) {
|
||||
Label unreachable(this), done(this);
|
||||
Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this),
|
||||
uint16_elements(this), int16_elements(this), uint32_elements(this),
|
||||
int32_elements(this), float32_elements(this), float64_elements(this);
|
||||
Label* elements_kind_labels[] = {
|
||||
&uint8_elements, &uint8_clamped_elements, &int8_elements,
|
||||
&uint16_elements, &int16_elements, &uint32_elements,
|
||||
&int32_elements, &float32_elements, &float64_elements};
|
||||
int32_t elements_kinds[] = {
|
||||
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
|
||||
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
|
||||
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
|
||||
const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
|
||||
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
|
||||
1;
|
||||
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
|
||||
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
|
||||
|
||||
Node* array_map = LoadMap(array);
|
||||
Node* elements_kind = LoadMapElementsKind(array_map);
|
||||
Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels,
|
||||
kTypedElementsKindCount);
|
||||
|
||||
for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) {
|
||||
Bind(elements_kind_labels[i]);
|
||||
{
|
||||
ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]);
|
||||
ExternalArrayType type =
|
||||
isolate()->factory()->GetArrayTypeFromElementsKind(kind);
|
||||
Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type));
|
||||
typed_map->Bind(HeapConstant(map));
|
||||
size->Bind(SmiConstant(static_cast<int>(
|
||||
isolate()->factory()->GetExternalArrayElementSize(type))));
|
||||
Goto(&done);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&unreachable);
|
||||
{ Unreachable(); }
|
||||
Bind(&done);
|
||||
}
|
||||
|
||||
// The byte_offset can be higher than Smi range, in which case to perform the
|
||||
// pointer arithmetic necessary to calculate external_pointer, converting
|
||||
// byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
|
||||
// on the particular platform. 32 bit platforms are self-limiting, because we
|
||||
// can't allocate an array bigger than our 32-bit arithmetic range anyway. 64
|
||||
// bit platforms could theoretically have an offset up to 2^35 - 1, so we may
|
||||
// need to convert the float heap number to an intptr.
|
||||
void TypedArrayBuiltinsAssembler::CalculateExternalPointer(
|
||||
Node* const backing_store, Node* const byte_offset,
|
||||
Variable* external_pointer) {
|
||||
Label offset_is_smi(this), offset_not_smi(this), done(this);
|
||||
Branch(TaggedIsSmi(byte_offset), &offset_is_smi, &offset_not_smi);
|
||||
|
||||
Bind(&offset_is_smi);
|
||||
{
|
||||
external_pointer->Bind(IntPtrAdd(backing_store, SmiToWord(byte_offset)));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&offset_not_smi);
|
||||
{
|
||||
Node* heap_number = LoadHeapNumberValue(byte_offset);
|
||||
Node* intrptr_value = ChangeFloat64ToUintPtr(heap_number);
|
||||
external_pointer->Bind(IntPtrAdd(backing_store, intrptr_value));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
}
|
||||
|
||||
void TypedArrayBuiltinsAssembler::DoInitialize(Node* const holder, Node* length,
|
||||
Node* const maybe_buffer,
|
||||
Node* const byte_offset,
|
||||
Node* byte_length,
|
||||
Node* const initialize,
|
||||
Node* const context) {
|
||||
static const int32_t fta_base_data_offset =
|
||||
FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
|
||||
|
||||
Label setup_holder(this), alloc_array_buffer(this), aligned(this),
|
||||
allocate_elements(this), attach_buffer(this), done(this);
|
||||
Variable fixed_typed_map(this, MachineRepresentation::kTagged);
|
||||
Variable element_size(this, MachineRepresentation::kTagged);
|
||||
Variable total_size(this, MachineType::PointerRepresentation());
|
||||
|
||||
// Make sure length is a Smi. The caller guarantees this is the case.
|
||||
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
|
||||
CSA_ASSERT(this, TaggedIsSmi(length));
|
||||
|
||||
// byte_length can be -0, get rid of it.
|
||||
byte_length =
|
||||
ToInteger(context, byte_length, CodeStubAssembler::kTruncateMinusZero);
|
||||
|
||||
GotoIfNot(IsNull(maybe_buffer), &setup_holder);
|
||||
// If the buffer is null, then we need a Smi byte_length. The caller
|
||||
// guarantees this is the case, because when byte_length >
|
||||
// TypedArrayMaxSizeInHeap, a buffer is allocated and passed in here.
|
||||
CSA_ASSERT(this, TaggedIsSmi(byte_length));
|
||||
Goto(&setup_holder);
|
||||
|
||||
Bind(&setup_holder);
|
||||
{
|
||||
LoadMapAndElementsSize(holder, &fixed_typed_map, &element_size);
|
||||
// Setup the holder (JSArrayBufferView).
|
||||
// - Set the length.
|
||||
// - Set the byte_offset.
|
||||
// - Set the byte_length.
|
||||
// - Set InternalFields to 0.
|
||||
StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
|
||||
StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset);
|
||||
StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length);
|
||||
for (int offset = JSTypedArray::kSize;
|
||||
offset < JSTypedArray::kSizeWithInternalFields;
|
||||
offset += kPointerSize) {
|
||||
StoreObjectField(holder, offset, SmiConstant(Smi::kZero));
|
||||
}
|
||||
|
||||
Branch(IsNull(maybe_buffer), &alloc_array_buffer, &attach_buffer);
|
||||
}
|
||||
|
||||
Bind(&alloc_array_buffer);
|
||||
{
|
||||
// Allocate a new ArrayBuffer and initialize it with empty properties and
|
||||
// elements.
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const map =
|
||||
LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
|
||||
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
|
||||
|
||||
Node* const buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields);
|
||||
StoreMapNoWriteBarrier(buffer, map);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset,
|
||||
empty_fixed_array);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
|
||||
empty_fixed_array);
|
||||
// Setup the ArrayBuffer.
|
||||
// - Set BitField to 0.
|
||||
// - Set IsExternal and IsNeuterable bits of BitFieldSlot.
|
||||
// - Set the byte_length field to byte_length.
|
||||
// - Set backing_store to null/Smi(0).
|
||||
// - Set all internal fields to Smi(0).
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot,
|
||||
SmiConstant(Smi::kZero));
|
||||
int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) |
|
||||
(1 << JSArrayBuffer::IsNeuterable::kShift);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
|
||||
Int32Constant(bitfield_value),
|
||||
MachineRepresentation::kWord32);
|
||||
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
|
||||
byte_length);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
|
||||
SmiConstant(Smi::kZero));
|
||||
for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
|
||||
int offset = JSArrayBuffer::kSize + i * kPointerSize;
|
||||
StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(Smi::kZero));
|
||||
}
|
||||
|
||||
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
|
||||
|
||||
// Check the alignment.
|
||||
GotoIf(SmiEqual(SmiMod(element_size.value(), SmiConstant(kObjectAlignment)),
|
||||
SmiConstant(0)),
|
||||
&aligned);
|
||||
|
||||
// Fix alignment if needed.
|
||||
DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
|
||||
Node* aligned_header_size =
|
||||
IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
|
||||
Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size);
|
||||
total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask)));
|
||||
Goto(&allocate_elements);
|
||||
}
|
||||
|
||||
Bind(&aligned);
|
||||
{
|
||||
Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
|
||||
total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size));
|
||||
Goto(&allocate_elements);
|
||||
}
|
||||
|
||||
Bind(&allocate_elements);
|
||||
{
|
||||
// Allocate a FixedTypedArray and set the length, base pointer and external
|
||||
// pointer.
|
||||
CSA_ASSERT(this, IsRegularHeapObjectSize(total_size.value()));
|
||||
Node* elements = Allocate(total_size.value());
|
||||
|
||||
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
|
||||
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kBasePointerOffset, elements);
|
||||
StoreObjectFieldNoWriteBarrier(elements,
|
||||
FixedTypedArrayBase::kExternalPointerOffset,
|
||||
IntPtrConstant(fta_base_data_offset),
|
||||
MachineType::PointerRepresentation());
|
||||
|
||||
StoreObjectField(holder, JSObject::kElementsOffset, elements);
|
||||
|
||||
GotoIf(IsFalse(initialize), &done);
|
||||
// Initialize the backing store by filling it with 0s.
|
||||
Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
|
||||
IntPtrConstant(fta_base_data_offset));
|
||||
// Call out to memset to perform initialization.
|
||||
Node* memset =
|
||||
ExternalConstant(ExternalReference::libc_memset_function(isolate()));
|
||||
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
|
||||
MachineType::IntPtr(), MachineType::UintPtr(), memset,
|
||||
backing_store, IntPtrConstant(0), SmiToWord(byte_length));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&attach_buffer);
|
||||
{
|
||||
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, maybe_buffer);
|
||||
|
||||
Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
|
||||
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
|
||||
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
|
||||
|
||||
Variable external_pointer(this, MachineType::PointerRepresentation());
|
||||
Node* backing_store =
|
||||
LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset,
|
||||
MachineType::Pointer());
|
||||
|
||||
CalculateExternalPointer(backing_store, byte_offset, &external_pointer);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kExternalPointerOffset,
|
||||
external_pointer.value(), MachineType::PointerRepresentation());
|
||||
|
||||
StoreObjectField(holder, JSObject::kElementsOffset, elements);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
|
||||
Node* const holder = Parameter(1);
|
||||
Node* length = Parameter(2);
|
||||
Node* const maybe_buffer = Parameter(3);
|
||||
Node* const byte_offset = Parameter(4);
|
||||
Node* byte_length = Parameter(5);
|
||||
Node* const initialize = Parameter(6);
|
||||
Node* const context = Parameter(9);
|
||||
|
||||
DoInitialize(holder, length, maybe_buffer, byte_offset, byte_length,
|
||||
initialize, context);
|
||||
}
|
||||
|
||||
// ES6 section 22.2.4.2 TypedArray ( length )
|
||||
TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
|
||||
// We know that holder cannot be an object if this builtin was called.
|
||||
Node* holder = Parameter(1);
|
||||
Node* length = Parameter(2);
|
||||
Node* element_size = Parameter(3);
|
||||
Node* context = Parameter(6);
|
||||
|
||||
Variable maybe_buffer(this, MachineRepresentation::kTagged);
|
||||
maybe_buffer.Bind(NullConstant());
|
||||
Node* byte_offset = SmiConstant(0);
|
||||
Node* initialize = BooleanConstant(true);
|
||||
|
||||
Label external_buffer(this), call_init(this), invalid_length(this);
|
||||
|
||||
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
|
||||
// The maximum length of a TypedArray is MaxSmi().
|
||||
// Note: this is not per spec, but rather a constraint of our current
|
||||
// representation (which uses smi's).
|
||||
GotoIf(TaggedIsNotSmi(length), &invalid_length);
|
||||
GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length);
|
||||
|
||||
// For byte_length < typed_array_max_size_in_heap, we allocate the buffer on
|
||||
// the heap. Otherwise we allocate it externally and attach it.
|
||||
Node* byte_length = SmiMul(length, element_size);
|
||||
GotoIf(TaggedIsNotSmi(byte_length), &external_buffer);
|
||||
Branch(SmiLessThanOrEqual(byte_length,
|
||||
SmiConstant(FLAG_typed_array_max_size_in_heap)),
|
||||
&call_init, &external_buffer);
|
||||
|
||||
Bind(&external_buffer);
|
||||
{
|
||||
Node* const buffer_constructor = LoadContextElement(
|
||||
LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX);
|
||||
maybe_buffer.Bind(ConstructJS(CodeFactory::Construct(isolate()), context,
|
||||
buffer_constructor, byte_length));
|
||||
Goto(&call_init);
|
||||
}
|
||||
|
||||
Bind(&call_init);
|
||||
{
|
||||
DoInitialize(holder, length, maybe_buffer.value(), byte_offset, byte_length,
|
||||
initialize, context);
|
||||
}
|
||||
|
||||
Bind(&invalid_length);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowRangeError, context,
|
||||
SmiConstant(MessageTemplate::kInvalidTypedArrayLength));
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
|
||||
TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
|
||||
Node* const holder = Parameter(1);
|
||||
Node* const buffer = Parameter(2);
|
||||
Node* byte_offset = Parameter(3);
|
||||
Node* const length = Parameter(4);
|
||||
Node* const element_size = Parameter(5);
|
||||
CSA_ASSERT(this, TaggedIsSmi(element_size));
|
||||
Node* const context = Parameter(8);
|
||||
Node* const initialize = BooleanConstant(true);
|
||||
|
||||
Variable new_byte_length(this, MachineRepresentation::kTagged,
|
||||
SmiConstant(0));
|
||||
|
||||
Label start_offset_error(this), byte_length_error(this),
|
||||
invalid_offset_error(this);
|
||||
Label call_init(this), invalid_length(this), length_undefined(this),
|
||||
length_defined(this);
|
||||
|
||||
Callable add = CodeFactory::Add(isolate());
|
||||
Callable div = CodeFactory::Divide(isolate());
|
||||
Callable equal = CodeFactory::Equal(isolate());
|
||||
Callable greater_than = CodeFactory::GreaterThan(isolate());
|
||||
Callable less_than = CodeFactory::LessThan(isolate());
|
||||
Callable mod = CodeFactory::Modulus(isolate());
|
||||
Callable sub = CodeFactory::Subtract(isolate());
|
||||
|
||||
byte_offset =
|
||||
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero);
|
||||
GotoIf(IsTrue(CallStub(less_than, context, byte_offset, SmiConstant(0))),
|
||||
&invalid_length);
|
||||
|
||||
Node* remainder = CallStub(mod, context, byte_offset, element_size);
|
||||
// Remainder can be a heap number.
|
||||
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
|
||||
&start_offset_error);
|
||||
|
||||
// TODO(petermarshall): Throw on detached typedArray.
|
||||
Branch(IsUndefined(length), &length_undefined, &length_defined);
|
||||
|
||||
Bind(&length_undefined);
|
||||
{
|
||||
Node* buffer_byte_length =
|
||||
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
|
||||
|
||||
Node* remainder = CallStub(mod, context, buffer_byte_length, element_size);
|
||||
// Remainder can be a heap number.
|
||||
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
|
||||
&byte_length_error);
|
||||
|
||||
new_byte_length.Bind(
|
||||
CallStub(sub, context, buffer_byte_length, byte_offset));
|
||||
|
||||
Branch(IsTrue(CallStub(less_than, context, new_byte_length.value(),
|
||||
SmiConstant(0))),
|
||||
&invalid_offset_error, &call_init);
|
||||
}
|
||||
|
||||
Bind(&length_defined);
|
||||
{
|
||||
Node* new_length = ToSmiIndex(length, context, &invalid_length);
|
||||
new_byte_length.Bind(SmiMul(new_length, element_size));
|
||||
// Reading the byte length must come after the ToIndex operation, which
|
||||
// could cause the buffer to become detached.
|
||||
Node* buffer_byte_length =
|
||||
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
|
||||
|
||||
Node* end = CallStub(add, context, byte_offset, new_byte_length.value());
|
||||
|
||||
Branch(IsTrue(CallStub(greater_than, context, end, buffer_byte_length)),
|
||||
&invalid_length, &call_init);
|
||||
}
|
||||
|
||||
Bind(&call_init);
|
||||
{
|
||||
Node* new_length =
|
||||
CallStub(div, context, new_byte_length.value(), element_size);
|
||||
// Force the result into a Smi, or throw a range error if it doesn't fit.
|
||||
new_length = ToSmiIndex(new_length, context, &invalid_length);
|
||||
|
||||
DoInitialize(holder, new_length, buffer, byte_offset,
|
||||
new_byte_length.value(), initialize, context);
|
||||
}
|
||||
|
||||
Bind(&invalid_offset_error);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowRangeError, context,
|
||||
SmiConstant(MessageTemplate::kInvalidOffset), byte_offset);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(&start_offset_error);
|
||||
{
|
||||
Node* holder_map = LoadMap(holder);
|
||||
Node* problem_string = HeapConstant(
|
||||
factory()->NewStringFromAsciiChecked("start offset", TENURED));
|
||||
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
|
||||
problem_string);
|
||||
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(&byte_length_error);
|
||||
{
|
||||
Node* holder_map = LoadMap(holder);
|
||||
Node* problem_string = HeapConstant(
|
||||
factory()->NewStringFromAsciiChecked("byte length", TENURED));
|
||||
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
|
||||
problem_string);
|
||||
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(&invalid_length);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowRangeError, context,
|
||||
SmiConstant(MessageTemplate::kInvalidTypedArrayLength));
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter(
|
||||
const char* method_name, int object_offset) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
// Check if the {receiver} is actually a JSTypedArray.
|
||||
Label receiver_is_incompatible(this, Label::kDeferred);
|
||||
GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible);
|
||||
GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE),
|
||||
&receiver_is_incompatible);
|
||||
|
||||
// Check if the {receiver}'s JSArrayBuffer was neutered.
|
||||
Node* receiver_buffer =
|
||||
LoadObjectField(receiver, JSTypedArray::kBufferOffset);
|
||||
Label if_receiverisneutered(this, Label::kDeferred);
|
||||
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
|
||||
Return(LoadObjectField(receiver, object_offset));
|
||||
|
||||
Bind(&if_receiverisneutered);
|
||||
{
|
||||
// The {receiver}s buffer was neutered, default to zero.
|
||||
Return(SmiConstant(0));
|
||||
}
|
||||
|
||||
Bind(&receiver_is_incompatible);
|
||||
{
|
||||
// The {receiver} is not a valid JSTypedArray.
|
||||
CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
|
||||
HeapConstant(
|
||||
factory()->NewStringFromAsciiChecked(method_name, TENURED)),
|
||||
receiver);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.2 get %TypedArray%.prototype.byteLength
|
||||
TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteLength",
|
||||
JSTypedArray::kByteLengthOffset);
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.3 get %TypedArray%.prototype.byteOffset
|
||||
TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteOffset",
|
||||
JSTypedArray::kByteOffsetOffset);
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.18 get %TypedArray%.prototype.length
|
||||
TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.length",
|
||||
JSTypedArray::kLengthOffset);
|
||||
}
|
||||
|
||||
template <IterationKind kIterationKind>
|
||||
void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
|
||||
const char* method_name) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Label throw_bad_receiver(this, Label::kDeferred);
|
||||
Label throw_typeerror(this, Label::kDeferred);
|
||||
|
||||
GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
|
||||
|
||||
Node* map = LoadMap(receiver);
|
||||
Node* instance_type = LoadMapInstanceType(map);
|
||||
GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
|
||||
&throw_bad_receiver);
|
||||
|
||||
// Check if the {receiver}'s JSArrayBuffer was neutered.
|
||||
Node* receiver_buffer =
|
||||
LoadObjectField(receiver, JSTypedArray::kBufferOffset);
|
||||
Label if_receiverisneutered(this, Label::kDeferred);
|
||||
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
|
||||
|
||||
Return(CreateArrayIterator(receiver, map, instance_type, context,
|
||||
kIterationKind));
|
||||
|
||||
Variable var_message(this, MachineRepresentation::kTagged);
|
||||
Bind(&throw_bad_receiver);
|
||||
var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray));
|
||||
Goto(&throw_typeerror);
|
||||
|
||||
Bind(&if_receiverisneutered);
|
||||
var_message.Bind(
|
||||
SmiConstant(Smi::FromInt(MessageTemplate::kDetachedOperation)));
|
||||
Goto(&throw_typeerror);
|
||||
|
||||
Bind(&throw_typeerror);
|
||||
{
|
||||
Node* method_arg = HeapConstant(
|
||||
isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
|
||||
Node* result = CallRuntime(Runtime::kThrowTypeError, context,
|
||||
var_message.value(), method_arg);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeIterationMethod<IterationKind::kValues>(
|
||||
"%TypedArray%.prototype.values()");
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeIterationMethod<IterationKind::kEntries>(
|
||||
"%TypedArray%.prototype.entries()");
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeIterationMethod<IterationKind::kKeys>(
|
||||
"%TypedArray%.prototype.keys()");
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/elements.h"
|
||||
#include "src/objects-inl.h"
|
||||
@ -12,463 +11,9 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void GenerateTypedArrayPrototypeGetter(const char* method_name,
|
||||
int object_offset);
|
||||
template <IterationKind kIterationKind>
|
||||
void GenerateTypedArrayPrototypeIterationMethod(const char* method_name);
|
||||
|
||||
void LoadMapAndElementsSize(Node* const array, Variable* typed_map,
|
||||
Variable* size);
|
||||
|
||||
void CalculateExternalPointer(Node* const backing_store,
|
||||
Node* const byte_offset,
|
||||
Variable* external_pointer);
|
||||
void DoInitialize(Node* const holder, Node* length, Node* const maybe_buffer,
|
||||
Node* const byte_offset, Node* byte_length,
|
||||
Node* const initialize, Node* const context);
|
||||
};
|
||||
|
||||
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* const array,
|
||||
Variable* typed_map,
|
||||
Variable* size) {
|
||||
Label unreachable(this), done(this);
|
||||
Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this),
|
||||
uint16_elements(this), int16_elements(this), uint32_elements(this),
|
||||
int32_elements(this), float32_elements(this), float64_elements(this);
|
||||
Label* elements_kind_labels[] = {
|
||||
&uint8_elements, &uint8_clamped_elements, &int8_elements,
|
||||
&uint16_elements, &int16_elements, &uint32_elements,
|
||||
&int32_elements, &float32_elements, &float64_elements};
|
||||
int32_t elements_kinds[] = {
|
||||
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
|
||||
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
|
||||
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
|
||||
const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
|
||||
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
|
||||
1;
|
||||
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
|
||||
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
|
||||
|
||||
Node* array_map = LoadMap(array);
|
||||
Node* elements_kind = LoadMapElementsKind(array_map);
|
||||
Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels,
|
||||
kTypedElementsKindCount);
|
||||
|
||||
for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) {
|
||||
Bind(elements_kind_labels[i]);
|
||||
{
|
||||
ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]);
|
||||
ExternalArrayType type =
|
||||
isolate()->factory()->GetArrayTypeFromElementsKind(kind);
|
||||
Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type));
|
||||
typed_map->Bind(HeapConstant(map));
|
||||
size->Bind(SmiConstant(static_cast<int>(
|
||||
isolate()->factory()->GetExternalArrayElementSize(type))));
|
||||
Goto(&done);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&unreachable);
|
||||
{ Unreachable(); }
|
||||
Bind(&done);
|
||||
}
|
||||
|
||||
// The byte_offset can be higher than Smi range, in which case to perform the
|
||||
// pointer arithmetic necessary to calculate external_pointer, converting
|
||||
// byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
|
||||
// on the particular platform. 32 bit platforms are self-limiting, because we
|
||||
// can't allocate an array bigger than our 32-bit arithmetic range anyway. 64
|
||||
// bit platforms could theoretically have an offset up to 2^35 - 1, so we may
|
||||
// need to convert the float heap number to an intptr.
|
||||
void TypedArrayBuiltinsAssembler::CalculateExternalPointer(
|
||||
Node* const backing_store, Node* const byte_offset,
|
||||
Variable* external_pointer) {
|
||||
Label offset_is_smi(this), offset_not_smi(this), done(this);
|
||||
Branch(TaggedIsSmi(byte_offset), &offset_is_smi, &offset_not_smi);
|
||||
|
||||
Bind(&offset_is_smi);
|
||||
{
|
||||
external_pointer->Bind(IntPtrAdd(backing_store, SmiToWord(byte_offset)));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&offset_not_smi);
|
||||
{
|
||||
Node* heap_number = LoadHeapNumberValue(byte_offset);
|
||||
Node* intrptr_value = ChangeFloat64ToUintPtr(heap_number);
|
||||
external_pointer->Bind(IntPtrAdd(backing_store, intrptr_value));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
}
|
||||
|
||||
void TypedArrayBuiltinsAssembler::DoInitialize(Node* const holder, Node* length,
|
||||
Node* const maybe_buffer,
|
||||
Node* const byte_offset,
|
||||
Node* byte_length,
|
||||
Node* const initialize,
|
||||
Node* const context) {
|
||||
static const int32_t fta_base_data_offset =
|
||||
FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
|
||||
|
||||
Label setup_holder(this), alloc_array_buffer(this), aligned(this),
|
||||
allocate_elements(this), attach_buffer(this), done(this);
|
||||
Variable fixed_typed_map(this, MachineRepresentation::kTagged);
|
||||
Variable element_size(this, MachineRepresentation::kTagged);
|
||||
Variable total_size(this, MachineType::PointerRepresentation());
|
||||
|
||||
// Make sure length is a Smi. The caller guarantees this is the case.
|
||||
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
|
||||
CSA_ASSERT(this, TaggedIsSmi(length));
|
||||
|
||||
// byte_length can be -0, get rid of it.
|
||||
byte_length =
|
||||
ToInteger(context, byte_length, CodeStubAssembler::kTruncateMinusZero);
|
||||
|
||||
GotoIfNot(IsNull(maybe_buffer), &setup_holder);
|
||||
// If the buffer is null, then we need a Smi byte_length. The caller
|
||||
// guarantees this is the case, because when byte_length >
|
||||
// TypedArrayMaxSizeInHeap, a buffer is allocated and passed in here.
|
||||
CSA_ASSERT(this, TaggedIsSmi(byte_length));
|
||||
Goto(&setup_holder);
|
||||
|
||||
Bind(&setup_holder);
|
||||
{
|
||||
LoadMapAndElementsSize(holder, &fixed_typed_map, &element_size);
|
||||
// Setup the holder (JSArrayBufferView).
|
||||
// - Set the length.
|
||||
// - Set the byte_offset.
|
||||
// - Set the byte_length.
|
||||
// - Set InternalFields to 0.
|
||||
StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
|
||||
StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset);
|
||||
StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length);
|
||||
for (int offset = JSTypedArray::kSize;
|
||||
offset < JSTypedArray::kSizeWithInternalFields;
|
||||
offset += kPointerSize) {
|
||||
StoreObjectField(holder, offset, SmiConstant(Smi::kZero));
|
||||
}
|
||||
|
||||
Branch(IsNull(maybe_buffer), &alloc_array_buffer, &attach_buffer);
|
||||
}
|
||||
|
||||
Bind(&alloc_array_buffer);
|
||||
{
|
||||
// Allocate a new ArrayBuffer and initialize it with empty properties and
|
||||
// elements.
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const map =
|
||||
LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
|
||||
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
|
||||
|
||||
Node* const buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields);
|
||||
StoreMapNoWriteBarrier(buffer, map);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset,
|
||||
empty_fixed_array);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
|
||||
empty_fixed_array);
|
||||
// Setup the ArrayBuffer.
|
||||
// - Set BitField to 0.
|
||||
// - Set IsExternal and IsNeuterable bits of BitFieldSlot.
|
||||
// - Set the byte_length field to byte_length.
|
||||
// - Set backing_store to null/Smi(0).
|
||||
// - Set all internal fields to Smi(0).
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot,
|
||||
SmiConstant(Smi::kZero));
|
||||
int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) |
|
||||
(1 << JSArrayBuffer::IsNeuterable::kShift);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
|
||||
Int32Constant(bitfield_value),
|
||||
MachineRepresentation::kWord32);
|
||||
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
|
||||
byte_length);
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
|
||||
SmiConstant(Smi::kZero));
|
||||
for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
|
||||
int offset = JSArrayBuffer::kSize + i * kPointerSize;
|
||||
StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(Smi::kZero));
|
||||
}
|
||||
|
||||
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
|
||||
|
||||
// Check the alignment.
|
||||
GotoIf(SmiEqual(SmiMod(element_size.value(), SmiConstant(kObjectAlignment)),
|
||||
SmiConstant(0)),
|
||||
&aligned);
|
||||
|
||||
// Fix alignment if needed.
|
||||
DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
|
||||
Node* aligned_header_size =
|
||||
IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
|
||||
Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size);
|
||||
total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask)));
|
||||
Goto(&allocate_elements);
|
||||
}
|
||||
|
||||
Bind(&aligned);
|
||||
{
|
||||
Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
|
||||
total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size));
|
||||
Goto(&allocate_elements);
|
||||
}
|
||||
|
||||
Bind(&allocate_elements);
|
||||
{
|
||||
// Allocate a FixedTypedArray and set the length, base pointer and external
|
||||
// pointer.
|
||||
CSA_ASSERT(this, IsRegularHeapObjectSize(total_size.value()));
|
||||
Node* elements = Allocate(total_size.value());
|
||||
|
||||
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
|
||||
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kBasePointerOffset, elements);
|
||||
StoreObjectFieldNoWriteBarrier(elements,
|
||||
FixedTypedArrayBase::kExternalPointerOffset,
|
||||
IntPtrConstant(fta_base_data_offset),
|
||||
MachineType::PointerRepresentation());
|
||||
|
||||
StoreObjectField(holder, JSObject::kElementsOffset, elements);
|
||||
|
||||
GotoIf(IsFalse(initialize), &done);
|
||||
// Initialize the backing store by filling it with 0s.
|
||||
Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
|
||||
IntPtrConstant(fta_base_data_offset));
|
||||
// Call out to memset to perform initialization.
|
||||
Node* memset =
|
||||
ExternalConstant(ExternalReference::libc_memset_function(isolate()));
|
||||
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
|
||||
MachineType::IntPtr(), MachineType::UintPtr(), memset,
|
||||
backing_store, IntPtrConstant(0), SmiToWord(byte_length));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&attach_buffer);
|
||||
{
|
||||
StoreObjectField(holder, JSArrayBufferView::kBufferOffset, maybe_buffer);
|
||||
|
||||
Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
|
||||
StoreMapNoWriteBarrier(elements, fixed_typed_map.value());
|
||||
StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
|
||||
|
||||
Variable external_pointer(this, MachineType::PointerRepresentation());
|
||||
Node* backing_store =
|
||||
LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset,
|
||||
MachineType::Pointer());
|
||||
|
||||
CalculateExternalPointer(backing_store, byte_offset, &external_pointer);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
elements, FixedTypedArrayBase::kExternalPointerOffset,
|
||||
external_pointer.value(), MachineType::PointerRepresentation());
|
||||
|
||||
StoreObjectField(holder, JSObject::kElementsOffset, elements);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
Bind(&done);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
|
||||
Node* const holder = Parameter(1);
|
||||
Node* length = Parameter(2);
|
||||
Node* const maybe_buffer = Parameter(3);
|
||||
Node* const byte_offset = Parameter(4);
|
||||
Node* byte_length = Parameter(5);
|
||||
Node* const initialize = Parameter(6);
|
||||
Node* const context = Parameter(9);
|
||||
|
||||
DoInitialize(holder, length, maybe_buffer, byte_offset, byte_length,
|
||||
initialize, context);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 22.2 TypedArray Objects
|
||||
|
||||
// ES6 section 22.2.4.2 TypedArray ( length )
|
||||
TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
|
||||
// We know that holder cannot be an object if this builtin was called.
|
||||
Node* holder = Parameter(1);
|
||||
Node* length = Parameter(2);
|
||||
Node* element_size = Parameter(3);
|
||||
Node* context = Parameter(6);
|
||||
|
||||
Variable maybe_buffer(this, MachineRepresentation::kTagged);
|
||||
maybe_buffer.Bind(NullConstant());
|
||||
Node* byte_offset = SmiConstant(0);
|
||||
Node* initialize = BooleanConstant(true);
|
||||
|
||||
Label external_buffer(this), call_init(this), invalid_length(this);
|
||||
|
||||
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
|
||||
// The maximum length of a TypedArray is MaxSmi().
|
||||
// Note: this is not per spec, but rather a constraint of our current
|
||||
// representation (which uses smi's).
|
||||
GotoIf(TaggedIsNotSmi(length), &invalid_length);
|
||||
GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length);
|
||||
|
||||
// For byte_length < typed_array_max_size_in_heap, we allocate the buffer on
|
||||
// the heap. Otherwise we allocate it externally and attach it.
|
||||
Node* byte_length = SmiMul(length, element_size);
|
||||
GotoIf(TaggedIsNotSmi(byte_length), &external_buffer);
|
||||
Branch(SmiLessThanOrEqual(byte_length,
|
||||
SmiConstant(FLAG_typed_array_max_size_in_heap)),
|
||||
&call_init, &external_buffer);
|
||||
|
||||
Bind(&external_buffer);
|
||||
{
|
||||
Node* const buffer_constructor = LoadContextElement(
|
||||
LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX);
|
||||
maybe_buffer.Bind(ConstructJS(CodeFactory::Construct(isolate()), context,
|
||||
buffer_constructor, byte_length));
|
||||
Goto(&call_init);
|
||||
}
|
||||
|
||||
Bind(&call_init);
|
||||
{
|
||||
DoInitialize(holder, length, maybe_buffer.value(), byte_offset, byte_length,
|
||||
initialize, context);
|
||||
}
|
||||
|
||||
Bind(&invalid_length);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowRangeError, context,
|
||||
SmiConstant(MessageTemplate::kInvalidTypedArrayLength));
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
|
||||
TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
|
||||
Node* const holder = Parameter(1);
|
||||
Node* const buffer = Parameter(2);
|
||||
Node* byte_offset = Parameter(3);
|
||||
Node* const length = Parameter(4);
|
||||
Node* const element_size = Parameter(5);
|
||||
CSA_ASSERT(this, TaggedIsSmi(element_size));
|
||||
Node* const context = Parameter(8);
|
||||
Node* const initialize = BooleanConstant(true);
|
||||
|
||||
Variable new_byte_length(this, MachineRepresentation::kTagged,
|
||||
SmiConstant(0));
|
||||
|
||||
Label start_offset_error(this), byte_length_error(this),
|
||||
invalid_offset_error(this);
|
||||
Label call_init(this), invalid_length(this), length_undefined(this),
|
||||
length_defined(this);
|
||||
|
||||
Callable add = CodeFactory::Add(isolate());
|
||||
Callable div = CodeFactory::Divide(isolate());
|
||||
Callable equal = CodeFactory::Equal(isolate());
|
||||
Callable greater_than = CodeFactory::GreaterThan(isolate());
|
||||
Callable less_than = CodeFactory::LessThan(isolate());
|
||||
Callable mod = CodeFactory::Modulus(isolate());
|
||||
Callable sub = CodeFactory::Subtract(isolate());
|
||||
|
||||
byte_offset =
|
||||
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero);
|
||||
GotoIf(IsTrue(CallStub(less_than, context, byte_offset, SmiConstant(0))),
|
||||
&invalid_length);
|
||||
|
||||
Node* remainder = CallStub(mod, context, byte_offset, element_size);
|
||||
// Remainder can be a heap number.
|
||||
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
|
||||
&start_offset_error);
|
||||
|
||||
// TODO(petermarshall): Throw on detached typedArray.
|
||||
Branch(IsUndefined(length), &length_undefined, &length_defined);
|
||||
|
||||
Bind(&length_undefined);
|
||||
{
|
||||
Node* buffer_byte_length =
|
||||
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
|
||||
|
||||
Node* remainder = CallStub(mod, context, buffer_byte_length, element_size);
|
||||
// Remainder can be a heap number.
|
||||
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
|
||||
&byte_length_error);
|
||||
|
||||
new_byte_length.Bind(
|
||||
CallStub(sub, context, buffer_byte_length, byte_offset));
|
||||
|
||||
Branch(IsTrue(CallStub(less_than, context, new_byte_length.value(),
|
||||
SmiConstant(0))),
|
||||
&invalid_offset_error, &call_init);
|
||||
}
|
||||
|
||||
Bind(&length_defined);
|
||||
{
|
||||
Node* new_length = ToSmiIndex(length, context, &invalid_length);
|
||||
new_byte_length.Bind(SmiMul(new_length, element_size));
|
||||
// Reading the byte length must come after the ToIndex operation, which
|
||||
// could cause the buffer to become detached.
|
||||
Node* buffer_byte_length =
|
||||
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
|
||||
|
||||
Node* end = CallStub(add, context, byte_offset, new_byte_length.value());
|
||||
|
||||
Branch(IsTrue(CallStub(greater_than, context, end, buffer_byte_length)),
|
||||
&invalid_length, &call_init);
|
||||
}
|
||||
|
||||
Bind(&call_init);
|
||||
{
|
||||
Node* new_length =
|
||||
CallStub(div, context, new_byte_length.value(), element_size);
|
||||
// Force the result into a Smi, or throw a range error if it doesn't fit.
|
||||
new_length = ToSmiIndex(new_length, context, &invalid_length);
|
||||
|
||||
DoInitialize(holder, new_length, buffer, byte_offset,
|
||||
new_byte_length.value(), initialize, context);
|
||||
}
|
||||
|
||||
Bind(&invalid_offset_error);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowRangeError, context,
|
||||
SmiConstant(MessageTemplate::kInvalidOffset), byte_offset);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(&start_offset_error);
|
||||
{
|
||||
Node* holder_map = LoadMap(holder);
|
||||
Node* problem_string = HeapConstant(
|
||||
factory()->NewStringFromAsciiChecked("start offset", TENURED));
|
||||
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
|
||||
problem_string);
|
||||
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(&byte_length_error);
|
||||
{
|
||||
Node* holder_map = LoadMap(holder);
|
||||
Node* problem_string = HeapConstant(
|
||||
factory()->NewStringFromAsciiChecked("byte length", TENURED));
|
||||
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
|
||||
problem_string);
|
||||
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
Bind(&invalid_length);
|
||||
{
|
||||
CallRuntime(Runtime::kThrowRangeError, context,
|
||||
SmiConstant(MessageTemplate::kInvalidTypedArrayLength));
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
|
||||
BUILTIN(TypedArrayPrototypeBuffer) {
|
||||
HandleScope scope(isolate);
|
||||
@ -476,119 +21,6 @@ BUILTIN(TypedArrayPrototypeBuffer) {
|
||||
return *typed_array->GetBuffer();
|
||||
}
|
||||
|
||||
void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter(
|
||||
const char* method_name, int object_offset) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
// Check if the {receiver} is actually a JSTypedArray.
|
||||
Label receiver_is_incompatible(this, Label::kDeferred);
|
||||
GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible);
|
||||
GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE),
|
||||
&receiver_is_incompatible);
|
||||
|
||||
// Check if the {receiver}'s JSArrayBuffer was neutered.
|
||||
Node* receiver_buffer =
|
||||
LoadObjectField(receiver, JSTypedArray::kBufferOffset);
|
||||
Label if_receiverisneutered(this, Label::kDeferred);
|
||||
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
|
||||
Return(LoadObjectField(receiver, object_offset));
|
||||
|
||||
Bind(&if_receiverisneutered);
|
||||
{
|
||||
// The {receiver}s buffer was neutered, default to zero.
|
||||
Return(SmiConstant(0));
|
||||
}
|
||||
|
||||
Bind(&receiver_is_incompatible);
|
||||
{
|
||||
// The {receiver} is not a valid JSTypedArray.
|
||||
CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
|
||||
HeapConstant(
|
||||
factory()->NewStringFromAsciiChecked(method_name, TENURED)),
|
||||
receiver);
|
||||
Unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.2 get %TypedArray%.prototype.byteLength
|
||||
TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteLength",
|
||||
JSTypedArray::kByteLengthOffset);
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.3 get %TypedArray%.prototype.byteOffset
|
||||
TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteOffset",
|
||||
JSTypedArray::kByteOffsetOffset);
|
||||
}
|
||||
|
||||
// ES6 section 22.2.3.18 get %TypedArray%.prototype.length
|
||||
TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.length",
|
||||
JSTypedArray::kLengthOffset);
|
||||
}
|
||||
|
||||
template <IterationKind kIterationKind>
|
||||
void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
|
||||
const char* method_name) {
|
||||
Node* receiver = Parameter(0);
|
||||
Node* context = Parameter(3);
|
||||
|
||||
Label throw_bad_receiver(this, Label::kDeferred);
|
||||
Label throw_typeerror(this, Label::kDeferred);
|
||||
|
||||
GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
|
||||
|
||||
Node* map = LoadMap(receiver);
|
||||
Node* instance_type = LoadMapInstanceType(map);
|
||||
GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
|
||||
&throw_bad_receiver);
|
||||
|
||||
// Check if the {receiver}'s JSArrayBuffer was neutered.
|
||||
Node* receiver_buffer =
|
||||
LoadObjectField(receiver, JSTypedArray::kBufferOffset);
|
||||
Label if_receiverisneutered(this, Label::kDeferred);
|
||||
GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
|
||||
|
||||
Return(CreateArrayIterator(receiver, map, instance_type, context,
|
||||
kIterationKind));
|
||||
|
||||
Variable var_message(this, MachineRepresentation::kTagged);
|
||||
Bind(&throw_bad_receiver);
|
||||
var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray));
|
||||
Goto(&throw_typeerror);
|
||||
|
||||
Bind(&if_receiverisneutered);
|
||||
var_message.Bind(
|
||||
SmiConstant(Smi::FromInt(MessageTemplate::kDetachedOperation)));
|
||||
Goto(&throw_typeerror);
|
||||
|
||||
Bind(&throw_typeerror);
|
||||
{
|
||||
Node* method_arg = HeapConstant(
|
||||
isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
|
||||
Node* result = CallRuntime(Runtime::kThrowTypeError, context,
|
||||
var_message.value(), method_arg);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeIterationMethod<IterationKind::kValues>(
|
||||
"%TypedArray%.prototype.values()");
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeIterationMethod<IterationKind::kEntries>(
|
||||
"%TypedArray%.prototype.entries()");
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
|
||||
GenerateTypedArrayPrototypeIterationMethod<IterationKind::kKeys>(
|
||||
"%TypedArray%.prototype.keys()");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
|
||||
|
43
src/builtins/builtins-utils-gen.h
Normal file
43
src/builtins/builtins-utils-gen.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
#ifndef V8_BUILTINS_BUILTINS_UTILS_GEN_H_
|
||||
#define V8_BUILTINS_BUILTINS_UTILS_GEN_H_
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace compiler {
|
||||
class CodeAssemblerState;
|
||||
} // namespace compiler
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support macro for defining builtins with Turbofan.
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// A builtin function is defined by writing:
|
||||
//
|
||||
// TF_BUILTIN(name, code_assember_base_class) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// In the body of the builtin function the arguments can be accessed
|
||||
// as "Parameter(n)".
|
||||
#define TF_BUILTIN(Name, AssemblerBase) \
|
||||
class Name##Assembler : public AssemblerBase { \
|
||||
public: \
|
||||
explicit Name##Assembler(compiler::CodeAssemblerState* state) \
|
||||
: AssemblerBase(state) {} \
|
||||
void Generate##Name##Impl(); \
|
||||
}; \
|
||||
void Builtins::Generate_##Name(compiler::CodeAssemblerState* state) { \
|
||||
Name##Assembler assembler(state); \
|
||||
assembler.Generate##Name##Impl(); \
|
||||
} \
|
||||
void Name##Assembler::Generate##Name##Impl()
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_BUILTINS_BUILTINS_UTILS_GEN_H_
|
@ -14,10 +14,6 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace compiler {
|
||||
class CodeAssemblerState;
|
||||
}
|
||||
|
||||
// Arguments object passed to C++ builtins.
|
||||
class BuiltinArguments : public Arguments {
|
||||
public:
|
||||
@ -106,31 +102,6 @@ class BuiltinArguments : public Arguments {
|
||||
MUST_USE_RESULT static Object* Builtin_Impl_##name(BuiltinArguments args, \
|
||||
Isolate* isolate)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support macro for defining builtins with Turbofan.
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// A builtin function is defined by writing:
|
||||
//
|
||||
// TF_BUILTIN(name, code_assember_base_class) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// In the body of the builtin function the arguments can be accessed
|
||||
// as "Parameter(n)".
|
||||
#define TF_BUILTIN(Name, AssemblerBase) \
|
||||
class Name##Assembler : public AssemblerBase { \
|
||||
public: \
|
||||
explicit Name##Assembler(compiler::CodeAssemblerState* state) \
|
||||
: AssemblerBase(state) {} \
|
||||
void Generate##Name##Impl(); \
|
||||
}; \
|
||||
void Builtins::Generate_##Name(compiler::CodeAssemblerState* state) { \
|
||||
Name##Assembler assembler(state); \
|
||||
assembler.Generate##Name##Impl(); \
|
||||
} \
|
||||
void Name##Assembler::Generate##Name##Impl()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define CHECK_RECEIVER(Type, name, method) \
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
@ -203,6 +203,77 @@ const char* Builtins::Lookup(byte* pc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
|
||||
switch (scope_type) {
|
||||
case ScopeType::EVAL_SCOPE:
|
||||
return FastNewFunctionContextEval();
|
||||
case ScopeType::FUNCTION_SCOPE:
|
||||
return FastNewFunctionContextFunction();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::NewCloneShallowArray(
|
||||
AllocationSiteMode allocation_mode) {
|
||||
switch (allocation_mode) {
|
||||
case TRACK_ALLOCATION_SITE:
|
||||
return FastCloneShallowArrayTrack();
|
||||
case DONT_TRACK_ALLOCATION_SITE:
|
||||
return FastCloneShallowArrayDontTrack();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::NewCloneShallowObject(int length) {
|
||||
switch (length) {
|
||||
case 0:
|
||||
return FastCloneShallowObject0();
|
||||
case 1:
|
||||
return FastCloneShallowObject1();
|
||||
case 2:
|
||||
return FastCloneShallowObject2();
|
||||
case 3:
|
||||
return FastCloneShallowObject3();
|
||||
case 4:
|
||||
return FastCloneShallowObject4();
|
||||
case 5:
|
||||
return FastCloneShallowObject5();
|
||||
case 6:
|
||||
return FastCloneShallowObject6();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
|
||||
switch (hint) {
|
||||
case ToPrimitiveHint::kDefault:
|
||||
return NonPrimitiveToPrimitive_Default();
|
||||
case ToPrimitiveHint::kNumber:
|
||||
return NonPrimitiveToPrimitive_Number();
|
||||
case ToPrimitiveHint::kString:
|
||||
return NonPrimitiveToPrimitive_String();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
|
||||
switch (hint) {
|
||||
case OrdinaryToPrimitiveHint::kNumber:
|
||||
return OrdinaryToPrimitive_Number();
|
||||
case OrdinaryToPrimitiveHint::kString:
|
||||
return OrdinaryToPrimitive_String();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
// static
|
||||
const char* Builtins::name(int index) {
|
||||
switch (index) {
|
||||
|
41
src/v8.gyp
41
src/v8.gyp
@ -471,49 +471,64 @@
|
||||
'bootstrapper.cc',
|
||||
'bootstrapper.h',
|
||||
'builtins/builtins-api.cc',
|
||||
'builtins/builtins-arguments.cc',
|
||||
'builtins/builtins-arguments-gen.cc',
|
||||
'builtins/builtins-arguments.h',
|
||||
'builtins/builtins-arraybuffer.cc',
|
||||
'builtins/builtins-array.cc',
|
||||
'builtins/builtins-async-iterator.cc',
|
||||
'builtins/builtins-async-function.cc',
|
||||
'builtins/builtins-async.cc',
|
||||
'builtins/builtins-array-gen.cc',
|
||||
'builtins/builtins-async-function-gen.cc',
|
||||
'builtins/builtins-async-iterator-gen.cc',
|
||||
'builtins/builtins-async-gen.cc',
|
||||
'builtins/builtins-async.h',
|
||||
'builtins/builtins-boolean.cc',
|
||||
'builtins/builtins-boolean-gen.cc',
|
||||
'builtins/builtins-call.cc',
|
||||
'builtins/builtins-callsite.cc',
|
||||
'builtins/builtins-conversion.cc',
|
||||
'builtins/builtins-constructor.cc',
|
||||
'builtins/builtins-constructor-gen.cc',
|
||||
'builtins/builtins-constructor.h',
|
||||
'builtins/builtins-conversion-gen.cc',
|
||||
'builtins/builtins-dataview.cc',
|
||||
'builtins/builtins-date.cc',
|
||||
'builtins/builtins-date-gen.cc',
|
||||
'builtins/builtins-debug.cc',
|
||||
'builtins/builtins-error.cc',
|
||||
'builtins/builtins-forin.cc',
|
||||
'builtins/builtins-forin-gen.cc',
|
||||
'builtins/builtins-forin.h',
|
||||
'builtins/builtins-function.cc',
|
||||
'builtins/builtins-generator.cc',
|
||||
'builtins/builtins-function-gen.cc',
|
||||
'builtins/builtins-generator-gen.cc',
|
||||
'builtins/builtins-global.cc',
|
||||
'builtins/builtins-handler.cc',
|
||||
'builtins/builtins-ic.cc',
|
||||
'builtins/builtins-global-gen.cc',
|
||||
'builtins/builtins-handler-gen.cc',
|
||||
'builtins/builtins-ic-gen.cc',
|
||||
'builtins/builtins-internal.cc',
|
||||
'builtins/builtins-internal-gen.cc',
|
||||
'builtins/builtins-interpreter.cc',
|
||||
'builtins/builtins-json.cc',
|
||||
'builtins/builtins-math.cc',
|
||||
'builtins/builtins-math-gen.cc',
|
||||
'builtins/builtins-number.cc',
|
||||
'builtins/builtins-number-gen.cc',
|
||||
'builtins/builtins-object.cc',
|
||||
'builtins/builtins-promise.cc',
|
||||
'builtins/builtins-object-gen.cc',
|
||||
'builtins/builtins-promise-gen.cc',
|
||||
'builtins/builtins-promise.h',
|
||||
'builtins/builtins-proxy.cc',
|
||||
'builtins/builtins-reflect.cc',
|
||||
'builtins/builtins-regexp.cc',
|
||||
'builtins/builtins-regexp.h',
|
||||
'builtins/builtins-regexp-gen.cc',
|
||||
'builtins/builtins-regexp-gen.h',
|
||||
'builtins/builtins-sharedarraybuffer.cc',
|
||||
'builtins/builtins-sharedarraybuffer-gen.cc',
|
||||
'builtins/builtins-string.cc',
|
||||
'builtins/builtins-string-gen.cc',
|
||||
'builtins/builtins-symbol.cc',
|
||||
'builtins/builtins-symbol-gen.cc',
|
||||
'builtins/builtins-typedarray.cc',
|
||||
'builtins/builtins-typedarray-gen.cc',
|
||||
'builtins/builtins-utils.h',
|
||||
'builtins/builtins-wasm.cc',
|
||||
'builtins/builtins-utils-gen.h',
|
||||
'builtins/builtins-wasm-gen.cc',
|
||||
'builtins/builtins.cc',
|
||||
'builtins/builtins.h',
|
||||
'cached-powers.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user