Commit d1d4fa20 authored by bmeurer's avatar bmeurer Committed by Commit bot

[runtime] Also migrate the Function and GeneratorFunction constructors to C++.

These constructors always go through C++ at least twice anyway, so
there's not really a point in trying to implement them in JavaScript.

R=yangguo@chromium.org
BUG=chromium:535408
LOG=n

Review URL: https://codereview.chromium.org/1548623002

Cr-Commit-Position: refs/heads/master@{#33012}
parent 739c0187
......@@ -1117,11 +1117,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{ // --- F u n c t i o n ---
Handle<JSFunction> function_function =
InstallFunction(global, "Function", JS_FUNCTION_TYPE, JSFunction::kSize,
empty_function, Builtins::kIllegal);
empty_function, Builtins::kFunctionConstructor);
function_function->set_prototype_or_initial_map(
*sloppy_function_map_writable_prototype_);
function_function->shared()->DontAdaptArguments();
function_function->shared()->set_construct_stub(
*isolate->builtins()->JSBuiltinsConstructStub());
*isolate->builtins()->FunctionConstructor());
function_function->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(isolate, function_function,
Context::FUNCTION_FUNCTION_INDEX);
......@@ -1909,14 +1911,16 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
generator_function_prototype, NONE);
static const bool kUseStrictFunctionMap = true;
Handle<JSFunction> generator_function_function =
InstallFunction(container, "GeneratorFunction", JS_FUNCTION_TYPE,
JSFunction::kSize, generator_function_prototype,
Builtins::kIllegal, kUseStrictFunctionMap);
Handle<JSFunction> generator_function_function = InstallFunction(
container, "GeneratorFunction", JS_FUNCTION_TYPE, JSFunction::kSize,
generator_function_prototype, Builtins::kGeneratorFunctionConstructor,
kUseStrictFunctionMap);
generator_function_function->set_prototype_or_initial_map(
native_context->sloppy_generator_function_map());
generator_function_function->shared()->DontAdaptArguments();
generator_function_function->shared()->set_construct_stub(
*isolate->builtins()->JSBuiltinsConstructStub());
*isolate->builtins()->GeneratorFunctionConstructor());
generator_function_function->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(
isolate, generator_function_function,
Context::GENERATOR_FUNCTION_FUNCTION_INDEX);
......
......@@ -19,6 +19,7 @@
#include "src/profiler/cpu-profiler.h"
#include "src/property-descriptor.h"
#include "src/prototype.h"
#include "src/string-builder.h"
#include "src/vm-state-inl.h"
namespace v8 {
......@@ -1503,7 +1504,6 @@ bool CodeGenerationFromStringsAllowed(Isolate* isolate,
}
// TODO(bmeurer): Also migrate the Function constructor to C++ and share this.
MaybeHandle<JSFunction> CompileString(Handle<Context> context,
Handle<String> source,
ParseRestriction restriction) {
......@@ -1846,6 +1846,131 @@ BUILTIN(DateToPrimitive) {
}
namespace {
// ES6 section 19.2.1.1.1 CreateDynamicFunction
MaybeHandle<JSFunction> CreateDynamicFunction(
Isolate* isolate,
BuiltinArguments<BuiltinExtraArguments::kTargetAndNewTarget> args,
const char* token) {
// Compute number of arguments, ignoring the receiver.
DCHECK_LE(1, args.length());
int const argc = args.length() - 1;
// Build the source string.
Handle<String> source;
{
IncrementalStringBuilder builder(isolate);
builder.AppendCharacter('(');
builder.AppendCString(token);
builder.AppendCharacter('(');
bool parenthesis_in_arg_string = false;
if (argc > 1) {
for (int i = 1; i < argc; ++i) {
if (i > 1) builder.AppendCharacter(',');
Handle<String> param;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, param, Object::ToString(isolate, args.at<Object>(i)),
JSFunction);
param = String::Flatten(param);
builder.AppendString(param);
// If the formal parameters string include ) - an illegal
// character - it may make the combined function expression
// compile. We avoid this problem by checking for this early on.
DisallowHeapAllocation no_gc; // Ensure vectors stay valid.
String::FlatContent param_content = param->GetFlatContent();
for (int i = 0, length = param->length(); i < length; ++i) {
if (param_content.Get(i) == ')') {
parenthesis_in_arg_string = true;
break;
}
}
}
// If the formal parameters include an unbalanced block comment, the
// function must be rejected. Since JavaScript does not allow nested
// comments we can include a trailing block comment to catch this.
builder.AppendCString("\n/**/");
}
builder.AppendCString(") {\n");
if (argc > 0) {
Handle<String> body;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, body, Object::ToString(isolate, args.at<Object>(argc)),
JSFunction);
builder.AppendString(body);
}
builder.AppendCString("\n})");
ASSIGN_RETURN_ON_EXCEPTION(isolate, source, builder.Finish(), JSFunction);
// The SyntaxError must be thrown after all the (observable) ToString
// conversions are done.
if (parenthesis_in_arg_string) {
THROW_NEW_ERROR(isolate,
NewSyntaxError(MessageTemplate::kParenthesisInArgString),
JSFunction);
}
}
// Compile the string in the constructor and not a helper so that errors to
// come from here.
Handle<JSFunction> target = args.target();
Handle<JSObject> target_global_proxy(target->global_proxy(), isolate);
Handle<JSFunction> function;
{
ASSIGN_RETURN_ON_EXCEPTION(
isolate, function,
CompileString(handle(target->native_context(), isolate), source,
ONLY_SINGLE_FUNCTION_LITERAL),
JSFunction);
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, function, target_global_proxy, 0, nullptr),
JSFunction);
function = Handle<JSFunction>::cast(result);
function->shared()->set_name_should_print_as_anonymous(true);
}
// If new.target is equal to target then the function created
// is already correctly setup and nothing else should be done
// here. But if new.target is not equal to target then we are
// have a Function builtin subclassing case and therefore the
// function has wrong initial map. To fix that we create a new
// function object with correct initial map.
Handle<Object> unchecked_new_target = args.new_target();
if (!unchecked_new_target->IsUndefined() &&
!unchecked_new_target.is_identical_to(target)) {
Handle<JSReceiver> new_target =
Handle<JSReceiver>::cast(unchecked_new_target);
Handle<Map> initial_map;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, initial_map,
JSFunction::GetDerivedMap(isolate, target, new_target), JSFunction);
Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
Handle<Map> map = Map::AsLanguageMode(
initial_map, shared_info->language_mode(), shared_info->kind());
Handle<Context> context(function->context(), isolate);
function = isolate->factory()->NewFunctionFromSharedFunctionInfo(
map, shared_info, context, NOT_TENURED);
}
return function;
}
} // namespace
// ES6 section 19.2.1.1 Function ( p1, p2, ... , pn, body )
BUILTIN(FunctionConstructor) {
HandleScope scope(isolate);
Handle<JSFunction> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, CreateDynamicFunction(isolate, args, "function"));
return *result;
}
// ES6 section 19.2.3.5 Function.prototype.toString ( )
BUILTIN(FunctionPrototypeToString) {
HandleScope scope(isolate);
......@@ -1861,6 +1986,16 @@ BUILTIN(FunctionPrototypeToString) {
}
// ES6 section 25.2.1.1 GeneratorFunction (p1, p2, ... , pn, body)
BUILTIN(GeneratorFunctionConstructor) {
HandleScope scope(isolate);
Handle<JSFunction> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, CreateDynamicFunction(isolate, args, "function*"));
return *result;
}
// ES6 section 19.4.1.1 Symbol ( [ description ] ) for the [[Call]] case.
BUILTIN(SymbolConstructor) {
HandleScope scope(isolate);
......
......@@ -51,53 +51,56 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
// Define list of builtins implemented in C++.
#define BUILTIN_LIST_C(V) \
V(Illegal, kNone) \
\
V(EmptyFunction, kNone) \
\
V(ArrayConcat, kNone) \
V(ArrayIsArray, kNone) \
V(ArrayPop, kNone) \
V(ArrayPush, kNone) \
V(ArrayShift, kNone) \
V(ArraySlice, kNone) \
V(ArraySplice, kNone) \
V(ArrayUnshift, kNone) \
\
V(DateToPrimitive, kNone) \
\
V(FunctionPrototypeToString, kNone) \
\
V(GlobalEval, kTarget) \
\
V(ObjectAssign, kNone) \
V(ObjectProtoToString, kNone) \
\
V(ProxyConstructor, kNone) \
V(ProxyConstructor_ConstructStub, kTarget) \
\
V(ReflectDefineProperty, kNone) \
V(ReflectDeleteProperty, kNone) \
V(ReflectGet, kNone) \
V(ReflectGetOwnPropertyDescriptor, kNone) \
V(ReflectGetPrototypeOf, kNone) \
V(ReflectHas, kNone) \
V(ReflectIsExtensible, kNone) \
V(ReflectOwnKeys, kNone) \
V(ReflectPreventExtensions, kNone) \
V(ReflectSet, kNone) \
V(ReflectSetPrototypeOf, kNone) \
\
V(SymbolConstructor, kNone) \
V(SymbolConstructor_ConstructStub, kTarget) \
\
V(HandleApiCall, kTarget) \
V(HandleApiCallConstruct, kTarget) \
V(HandleApiCallAsFunction, kNone) \
V(HandleApiCallAsConstructor, kNone) \
\
V(RestrictedFunctionPropertiesThrower, kNone) \
#define BUILTIN_LIST_C(V) \
V(Illegal, kNone) \
\
V(EmptyFunction, kNone) \
\
V(ArrayConcat, kNone) \
V(ArrayIsArray, kNone) \
V(ArrayPop, kNone) \
V(ArrayPush, kNone) \
V(ArrayShift, kNone) \
V(ArraySlice, kNone) \
V(ArraySplice, kNone) \
V(ArrayUnshift, kNone) \
\
V(DateToPrimitive, kNone) \
\
V(FunctionConstructor, kTargetAndNewTarget) \
V(FunctionPrototypeToString, kNone) \
\
V(GeneratorFunctionConstructor, kTargetAndNewTarget) \
\
V(GlobalEval, kTarget) \
\
V(ObjectAssign, kNone) \
V(ObjectProtoToString, kNone) \
\
V(ProxyConstructor, kNone) \
V(ProxyConstructor_ConstructStub, kTarget) \
\
V(ReflectDefineProperty, kNone) \
V(ReflectDeleteProperty, kNone) \
V(ReflectGet, kNone) \
V(ReflectGetOwnPropertyDescriptor, kNone) \
V(ReflectGetPrototypeOf, kNone) \
V(ReflectHas, kNone) \
V(ReflectIsExtensible, kNone) \
V(ReflectOwnKeys, kNone) \
V(ReflectPreventExtensions, kNone) \
V(ReflectSet, kNone) \
V(ReflectSetPrototypeOf, kNone) \
\
V(SymbolConstructor, kNone) \
V(SymbolConstructor_ConstructStub, kTarget) \
\
V(HandleApiCall, kTarget) \
V(HandleApiCallConstruct, kTarget) \
V(HandleApiCallAsFunction, kNone) \
V(HandleApiCallAsConstructor, kNone) \
\
V(RestrictedFunctionPropertiesThrower, kNone) \
V(RestrictedStrictArgumentsPropertiesThrower, kNone)
// Define list of builtins implemented in assembly.
......
......@@ -15,12 +15,10 @@ var GeneratorFunctionPrototype = utils.ImportNow("GeneratorFunctionPrototype");
var GeneratorFunction = utils.ImportNow("GeneratorFunction");
var GlobalFunction = global.Function;
var MakeTypeError;
var NewFunctionString;
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) {
MakeTypeError = from.MakeTypeError;
NewFunctionString = from.NewFunctionString;
});
// ----------------------------------------------------------------------------
......@@ -78,19 +76,6 @@ function GeneratorObjectThrow(exn) {
}
}
function GeneratorFunctionConstructor(arg1) { // length == 1
var source = NewFunctionString(arguments, 'function*');
var global_proxy = %GlobalProxy(GeneratorFunctionConstructor);
// Compile the string in the constructor and not a helper so that errors
// appear to come from here.
var func = %_Call(%CompileString(source, true), global_proxy);
// Set name-should-print-as-anonymous flag on the ShareFunctionInfo and
// ensure that |func| uses correct initial map from |new.target| if
// it's available.
return %CompleteFunctionConstruction(func, GeneratorFunction, new.target);
}
// ----------------------------------------------------------------------------
// Both Runtime_GeneratorNext and Runtime_GeneratorThrow are supported by
......@@ -115,6 +100,5 @@ utils.InstallFunctions(GeneratorObjectPrototype,
%AddNamedProperty(GeneratorFunctionPrototype, "constructor",
GeneratorFunction, DONT_ENUM | READ_ONLY);
%InternalSetPrototype(GeneratorFunction, GlobalFunction);
%SetCode(GeneratorFunction, GeneratorFunctionConstructor);
})
......@@ -26,7 +26,6 @@ var ObserveBeginPerformSplice;
var ObserveEndPerformSplice;
var ObserveEnqueueSpliceRecord;
var SameValue = utils.ImportNow("SameValue");
var StringIndexOf;
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) {
......@@ -37,7 +36,6 @@ utils.Import(function(from) {
ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
ObserveEndPerformSplice = from.ObserveEndPerformSplice;
ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
StringIndexOf = from.StringIndexOf;
});
// ----------------------------------------------------------------------------
......@@ -1304,46 +1302,8 @@ function FunctionBind(this_arg) { // Length is 1.
}
function NewFunctionString(args, function_token) {
var n = args.length;
var p = '';
if (n > 1) {
p = TO_STRING(args[0]);
for (var i = 1; i < n - 1; i++) {
p += ',' + TO_STRING(args[i]);
}
// If the formal parameters string include ) - an illegal
// character - it may make the combined function expression
// compile. We avoid this problem by checking for this early on.
if (%_Call(StringIndexOf, p, ')') != -1) {
throw MakeSyntaxError(kParenthesisInArgString);
}
// If the formal parameters include an unbalanced block comment, the
// function must be rejected. Since JavaScript does not allow nested
// comments we can include a trailing block comment to catch this.
p += '\n/' + '**/';
}
var body = (n > 0) ? TO_STRING(args[n - 1]) : '';
return '(' + function_token + '(' + p + ') {\n' + body + '\n})';
}
function FunctionConstructor(arg1) { // length == 1
var source = NewFunctionString(arguments, 'function');
var global_proxy = %GlobalProxy(FunctionConstructor);
// Compile the string in the constructor and not a helper so that errors
// appear to come from here.
var func = %_Call(%CompileString(source, true), global_proxy);
// Set name-should-print-as-anonymous flag on the ShareFunctionInfo and
// ensure that |func| uses correct initial map from |new.target| if
// it's available.
return %CompleteFunctionConstruction(func, GlobalFunction, new.target);
}
// ----------------------------------------------------------------------------
%SetCode(GlobalFunction, FunctionConstructor);
%AddNamedProperty(GlobalFunction.prototype, "constructor", GlobalFunction,
DONT_ENUM);
......@@ -1377,7 +1337,6 @@ utils.Export(function(to) {
to.GetMethod = GetMethod;
to.IsFinite = GlobalIsFinite;
to.IsNaN = GlobalIsNaN;
to.NewFunctionString = NewFunctionString;
to.NumberIsNaN = NumberIsNaN;
to.ObjectDefineProperties = ObjectDefineProperties;
to.ObjectDefineProperty = ObjectDefineProperty;
......
......@@ -366,40 +366,6 @@ bool CodeGenerationFromStringsAllowed(Isolate* isolate,
}
RUNTIME_FUNCTION(Runtime_CompileString) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
CONVERT_BOOLEAN_ARG_CHECKED(function_literal_only, 1);
// Extract native context.
Handle<Context> context(isolate->native_context());
// Check if native context allows code generation from
// strings. Throw an exception if it doesn't.
if (context->allow_code_gen_from_strings()->IsFalse() &&
!CodeGenerationFromStringsAllowed(isolate, context)) {
Handle<Object> error_message =
context->ErrorMessageForCodeGenerationFromStrings();
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewEvalError(MessageTemplate::kCodeGenFromStrings, error_message));
}
// Compile source string in the native context.
ParseRestriction restriction = function_literal_only
? ONLY_SINGLE_FUNCTION_LITERAL
: NO_PARSE_RESTRICTION;
Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
Handle<JSFunction> fun;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, fun,
Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY,
restriction, RelocInfo::kNoPosition));
return *fun;
}
static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
LanguageMode language_mode,
......
......@@ -38,48 +38,6 @@ RUNTIME_FUNCTION(Runtime_FunctionSetName) {
}
RUNTIME_FUNCTION(Runtime_CompleteFunctionConstruction) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, unchecked_new_target, 2);
func->shared()->set_name_should_print_as_anonymous(true);
if (unchecked_new_target->IsUndefined()) return *func;
Handle<JSReceiver> new_target =
Handle<JSReceiver>::cast(unchecked_new_target);
// If new.target is equal to |constructor| then the function |func| created
// is already correctly setup and nothing else should be done here.
// But if new.target is not equal to |constructor| then we are have a
// Function builtin subclassing case and therefore the function |func|
// has wrong initial map. To fix that we create a new function object with
// correct initial map.
if (*constructor == *new_target) return *func;
// Create a new JSFunction object with correct initial map.
HandleScope handle_scope(isolate);
DCHECK(constructor->has_initial_map());
Handle<Map> initial_map;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, initial_map,
JSFunction::GetDerivedMap(isolate, constructor, new_target));
Handle<SharedFunctionInfo> shared_info(func->shared(), isolate);
Handle<Map> map = Map::AsLanguageMode(
initial_map, shared_info->language_mode(), shared_info->kind());
Handle<Context> context(func->context(), isolate);
Handle<JSFunction> result =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
map, shared_info, context, NOT_TENURED);
DCHECK_EQ(func->IsConstructor(), result->IsConstructor());
return *result;
}
RUNTIME_FUNCTION(Runtime_FunctionRemovePrototype) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
......
......@@ -132,7 +132,6 @@ namespace internal {
F(NotifyDeoptimized, 1, 1) \
F(CompileForOnStackReplacement, 1, 1) \
F(TryInstallOptimizedCode, 1, 1) \
F(CompileString, 2, 1) \
F(ResolvePossiblyDirectEval, 5, 1)
......@@ -234,7 +233,6 @@ namespace internal {
#define FOR_EACH_INTRINSIC_FUNCTION(F) \
F(FunctionGetName, 1, 1) \
F(FunctionSetName, 2, 1) \
F(CompleteFunctionConstruction, 3, 1) \
F(FunctionRemovePrototype, 1, 1) \
F(FunctionGetScript, 1, 1) \
F(FunctionGetSourceCode, 1, 1) \
......
......@@ -2,5 +2,4 @@
var paren_in_arg_string_bad = new Function(')', 'return;');
^
SyntaxError: Function arg string contains parenthesis
at Function (native)
at *%(basename)s:29:31
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax
var single_function_good = "(function() { return 5; })";
%CompileString(single_function_good, true);
var single_function_bad = "(function() { return 5; })();";
%CompileString(single_function_bad, true);
undefined:1: SyntaxError: Single function literal required
(function() { return 5; })();
^
SyntaxError: Single function literal required
at *%(basename)s:32:16
\ No newline at end of file
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Return the raw CallSites array.
Error.prepareStackTrace = function (a,b) { return b; };
var threw = false;
try {
new Function({toString:0,valueOf:0});
} catch (e) {
threw = true;
// Ensure that the receiver during "new Function" is the undefined value.
assertEquals(undefined, e.stack[0].getThis());
}
assertTrue(threw);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment