1 // Copyright 2016 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/builtins/builtins.h" 6 #include "src/builtins/builtins-utils.h" 7 8 #include "src/compiler.h" 9 #include "src/string-builder.h" 10 11 namespace v8 { 12 namespace internal { 13 14 namespace { 15 16 // ES6 section 19.2.1.1.1 CreateDynamicFunction 17 MaybeHandle<Object> CreateDynamicFunction(Isolate* isolate, 18 BuiltinArguments args, 19 const char* token) { 20 // Compute number of arguments, ignoring the receiver. 21 DCHECK_LE(1, args.length()); 22 int const argc = args.length() - 1; 23 24 Handle<JSFunction> target = args.target(); 25 Handle<JSObject> target_global_proxy(target->global_proxy(), isolate); 26 27 if (!Builtins::AllowDynamicFunction(isolate, target, target_global_proxy)) { 28 isolate->CountUsage(v8::Isolate::kFunctionConstructorReturnedUndefined); 29 return isolate->factory()->undefined_value(); 30 } 31 32 // Build the source string. 33 Handle<String> source; 34 { 35 IncrementalStringBuilder builder(isolate); 36 builder.AppendCharacter('('); 37 builder.AppendCString(token); 38 builder.AppendCharacter('('); 39 bool parenthesis_in_arg_string = false; 40 if (argc > 1) { 41 for (int i = 1; i < argc; ++i) { 42 if (i > 1) builder.AppendCharacter(','); 43 Handle<String> param; 44 ASSIGN_RETURN_ON_EXCEPTION( 45 isolate, param, Object::ToString(isolate, args.at<Object>(i)), 46 Object); 47 param = String::Flatten(param); 48 builder.AppendString(param); 49 // If the formal parameters string include ) - an illegal 50 // character - it may make the combined function expression 51 // compile. We avoid this problem by checking for this early on. 52 DisallowHeapAllocation no_gc; // Ensure vectors stay valid. 53 String::FlatContent param_content = param->GetFlatContent(); 54 for (int i = 0, length = param->length(); i < length; ++i) { 55 if (param_content.Get(i) == ')') { 56 parenthesis_in_arg_string = true; 57 break; 58 } 59 } 60 } 61 // If the formal parameters include an unbalanced block comment, the 62 // function must be rejected. Since JavaScript does not allow nested 63 // comments we can include a trailing block comment to catch this. 64 builder.AppendCString("\n/**/"); 65 } 66 builder.AppendCString(") {\n"); 67 if (argc > 0) { 68 Handle<String> body; 69 ASSIGN_RETURN_ON_EXCEPTION( 70 isolate, body, Object::ToString(isolate, args.at<Object>(argc)), 71 Object); 72 builder.AppendString(body); 73 } 74 builder.AppendCString("\n})"); 75 ASSIGN_RETURN_ON_EXCEPTION(isolate, source, builder.Finish(), Object); 76 77 // The SyntaxError must be thrown after all the (observable) ToString 78 // conversions are done. 79 if (parenthesis_in_arg_string) { 80 THROW_NEW_ERROR(isolate, 81 NewSyntaxError(MessageTemplate::kParenthesisInArgString), 82 Object); 83 } 84 } 85 86 // Compile the string in the constructor and not a helper so that errors to 87 // come from here. 88 Handle<JSFunction> function; 89 { 90 ASSIGN_RETURN_ON_EXCEPTION(isolate, function, 91 Compiler::GetFunctionFromString( 92 handle(target->native_context(), isolate), 93 source, ONLY_SINGLE_FUNCTION_LITERAL), 94 Object); 95 Handle<Object> result; 96 ASSIGN_RETURN_ON_EXCEPTION( 97 isolate, result, 98 Execution::Call(isolate, function, target_global_proxy, 0, nullptr), 99 Object); 100 function = Handle<JSFunction>::cast(result); 101 function->shared()->set_name_should_print_as_anonymous(true); 102 } 103 104 // If new.target is equal to target then the function created 105 // is already correctly setup and nothing else should be done 106 // here. But if new.target is not equal to target then we are 107 // have a Function builtin subclassing case and therefore the 108 // function has wrong initial map. To fix that we create a new 109 // function object with correct initial map. 110 Handle<Object> unchecked_new_target = args.new_target(); 111 if (!unchecked_new_target->IsUndefined(isolate) && 112 !unchecked_new_target.is_identical_to(target)) { 113 Handle<JSReceiver> new_target = 114 Handle<JSReceiver>::cast(unchecked_new_target); 115 Handle<Map> initial_map; 116 ASSIGN_RETURN_ON_EXCEPTION( 117 isolate, initial_map, 118 JSFunction::GetDerivedMap(isolate, target, new_target), Object); 119 120 Handle<SharedFunctionInfo> shared_info(function->shared(), isolate); 121 Handle<Map> map = Map::AsLanguageMode( 122 initial_map, shared_info->language_mode(), shared_info->kind()); 123 124 Handle<Context> context(function->context(), isolate); 125 function = isolate->factory()->NewFunctionFromSharedFunctionInfo( 126 map, shared_info, context, NOT_TENURED); 127 } 128 return function; 129 } 130 131 } // namespace 132 133 // ES6 section 19.2.1.1 Function ( p1, p2, ... , pn, body ) 134 BUILTIN(FunctionConstructor) { 135 HandleScope scope(isolate); 136 Handle<Object> result; 137 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 138 isolate, result, CreateDynamicFunction(isolate, args, "function")); 139 return *result; 140 } 141 142 // ES6 section 25.2.1.1 GeneratorFunction (p1, p2, ... , pn, body) 143 BUILTIN(GeneratorFunctionConstructor) { 144 HandleScope scope(isolate); 145 RETURN_RESULT_OR_FAILURE(isolate, 146 CreateDynamicFunction(isolate, args, "function*")); 147 } 148 149 BUILTIN(AsyncFunctionConstructor) { 150 HandleScope scope(isolate); 151 Handle<Object> maybe_func; 152 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 153 isolate, maybe_func, 154 CreateDynamicFunction(isolate, args, "async function")); 155 if (!maybe_func->IsJSFunction()) return *maybe_func; 156 157 // Do not lazily compute eval position for AsyncFunction, as they may not be 158 // determined after the function is resumed. 159 Handle<JSFunction> func = Handle<JSFunction>::cast(maybe_func); 160 Handle<Script> script = handle(Script::cast(func->shared()->script())); 161 int position = script->GetEvalPosition(); 162 USE(position); 163 164 return *func; 165 } 166 167 namespace { 168 169 Object* DoFunctionBind(Isolate* isolate, BuiltinArguments args) { 170 HandleScope scope(isolate); 171 DCHECK_LE(1, args.length()); 172 if (!args.receiver()->IsCallable()) { 173 THROW_NEW_ERROR_RETURN_FAILURE( 174 isolate, NewTypeError(MessageTemplate::kFunctionBind)); 175 } 176 177 // Allocate the bound function with the given {this_arg} and {args}. 178 Handle<JSReceiver> target = args.at<JSReceiver>(0); 179 Handle<Object> this_arg = isolate->factory()->undefined_value(); 180 ScopedVector<Handle<Object>> argv(std::max(0, args.length() - 2)); 181 if (args.length() > 1) { 182 this_arg = args.at<Object>(1); 183 for (int i = 2; i < args.length(); ++i) { 184 argv[i - 2] = args.at<Object>(i); 185 } 186 } 187 Handle<JSBoundFunction> function; 188 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 189 isolate, function, 190 isolate->factory()->NewJSBoundFunction(target, this_arg, argv)); 191 192 LookupIterator length_lookup(target, isolate->factory()->length_string(), 193 target, LookupIterator::OWN); 194 // Setup the "length" property based on the "length" of the {target}. 195 // If the targets length is the default JSFunction accessor, we can keep the 196 // accessor that's installed by default on the JSBoundFunction. It lazily 197 // computes the value from the underlying internal length. 198 if (!target->IsJSFunction() || 199 length_lookup.state() != LookupIterator::ACCESSOR || 200 !length_lookup.GetAccessors()->IsAccessorInfo()) { 201 Handle<Object> length(Smi::kZero, isolate); 202 Maybe<PropertyAttributes> attributes = 203 JSReceiver::GetPropertyAttributes(&length_lookup); 204 if (!attributes.IsJust()) return isolate->heap()->exception(); 205 if (attributes.FromJust() != ABSENT) { 206 Handle<Object> target_length; 207 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, target_length, 208 Object::GetProperty(&length_lookup)); 209 if (target_length->IsNumber()) { 210 length = isolate->factory()->NewNumber(std::max( 211 0.0, DoubleToInteger(target_length->Number()) - argv.length())); 212 } 213 } 214 LookupIterator it(function, isolate->factory()->length_string(), function); 215 DCHECK_EQ(LookupIterator::ACCESSOR, it.state()); 216 RETURN_FAILURE_ON_EXCEPTION(isolate, 217 JSObject::DefineOwnPropertyIgnoreAttributes( 218 &it, length, it.property_attributes())); 219 } 220 221 // Setup the "name" property based on the "name" of the {target}. 222 // If the targets name is the default JSFunction accessor, we can keep the 223 // accessor that's installed by default on the JSBoundFunction. It lazily 224 // computes the value from the underlying internal name. 225 LookupIterator name_lookup(target, isolate->factory()->name_string(), target, 226 LookupIterator::OWN); 227 if (!target->IsJSFunction() || 228 name_lookup.state() != LookupIterator::ACCESSOR || 229 !name_lookup.GetAccessors()->IsAccessorInfo()) { 230 Handle<Object> target_name; 231 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, target_name, 232 Object::GetProperty(&name_lookup)); 233 Handle<String> name; 234 if (target_name->IsString()) { 235 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 236 isolate, name, 237 Name::ToFunctionName(Handle<String>::cast(target_name))); 238 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 239 isolate, name, isolate->factory()->NewConsString( 240 isolate->factory()->bound__string(), name)); 241 } else { 242 name = isolate->factory()->bound__string(); 243 } 244 LookupIterator it(function, isolate->factory()->name_string()); 245 DCHECK_EQ(LookupIterator::ACCESSOR, it.state()); 246 RETURN_FAILURE_ON_EXCEPTION(isolate, 247 JSObject::DefineOwnPropertyIgnoreAttributes( 248 &it, name, it.property_attributes())); 249 } 250 return *function; 251 } 252 253 } // namespace 254 255 // ES6 section 19.2.3.2 Function.prototype.bind ( thisArg, ...args ) 256 BUILTIN(FunctionPrototypeBind) { return DoFunctionBind(isolate, args); } 257 258 // TODO(verwaest): This is a temporary helper until the FastFunctionBind stub 259 // can tailcall to the builtin directly. 260 RUNTIME_FUNCTION(Runtime_FunctionBind) { 261 DCHECK_EQ(2, args.length()); 262 Arguments* incoming = reinterpret_cast<Arguments*>(args[0]); 263 // Rewrap the arguments as builtins arguments. 264 int argc = incoming->length() + BuiltinArguments::kNumExtraArgsWithReceiver; 265 BuiltinArguments caller_args(argc, incoming->arguments() + 1); 266 return DoFunctionBind(isolate, caller_args); 267 } 268 269 // ES6 section 19.2.3.5 Function.prototype.toString ( ) 270 BUILTIN(FunctionPrototypeToString) { 271 HandleScope scope(isolate); 272 Handle<Object> receiver = args.receiver(); 273 if (receiver->IsJSBoundFunction()) { 274 return *JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(receiver)); 275 } else if (receiver->IsJSFunction()) { 276 return *JSFunction::ToString(Handle<JSFunction>::cast(receiver)); 277 } 278 THROW_NEW_ERROR_RETURN_FAILURE( 279 isolate, NewTypeError(MessageTemplate::kNotGeneric, 280 isolate->factory()->NewStringFromAsciiChecked( 281 "Function.prototype.toString"))); 282 } 283 284 // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V ) 285 void Builtins::Generate_FunctionPrototypeHasInstance( 286 CodeStubAssembler* assembler) { 287 using compiler::Node; 288 289 Node* f = assembler->Parameter(0); 290 Node* v = assembler->Parameter(1); 291 Node* context = assembler->Parameter(4); 292 Node* result = assembler->OrdinaryHasInstance(context, f, v); 293 assembler->Return(result); 294 } 295 296 } // namespace internal 297 } // namespace v8 298