• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/builtins/builtins_function.h"
17 
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/jspandafile/js_pandafile_manager.h"
22 #include "ecmascript/js_arguments.h"
23 #include "ecmascript/js_stable_array.h"
24 #include "ecmascript/object_fast_operator-inl.h"
25 #include "ecmascript/tagged_array-inl.h"
26 
27 namespace panda::ecmascript::builtins {
28 // ecma 19.2.1 Function (p1, p2, ... , pn, body)
FunctionConstructor(EcmaRuntimeCallInfo * argv)29 JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv)
30 {
31     // not support
32     JSThread *thread = argv->GetThread();
33     BUILTINS_API_TRACE(thread, Function, Constructor);
34     [[maybe_unused]] EcmaHandleScope handleScope(thread);
35     THROW_TYPE_ERROR_AND_RETURN(thread, "Not support eval. Forbidden using new Function()/Function().",
36                                 JSTaggedValue::Exception());
37 }
38 
39 // ecma 19.2.3 The Function prototype object is itself a built-in function object.
40 //             When invoked, it accepts any arguments and returns undefined.
FunctionPrototypeInvokeSelf(EcmaRuntimeCallInfo * argv)41 JSTaggedValue BuiltinsFunction::FunctionPrototypeInvokeSelf([[maybe_unused]] EcmaRuntimeCallInfo *argv)
42 {
43     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeInvokeSelf);
44     return JSTaggedValue::Undefined();
45 }
46 namespace {
MakeArgListWithHole(JSThread * thread,TaggedArray * argv,size_t length)47 static size_t MakeArgListWithHole(JSThread *thread, TaggedArray *argv, size_t length)
48 {
49     if (length > argv->GetLength()) {
50         length = argv->GetLength();
51     }
52     for (size_t index = 0; index < length; ++index) {
53         JSTaggedValue value = argv->Get(thread, index);
54         if (value.IsHole()) {
55             argv->Set(thread, index, JSTaggedValue::Undefined());
56         }
57     }
58     return length;
59 }
60 
BuildArgumentsListFast(JSThread * thread,const JSHandle<JSTaggedValue> & arrayObj)61 static std::pair<TaggedArray*, size_t> BuildArgumentsListFast(JSThread *thread,
62                                                               const JSHandle<JSTaggedValue> &arrayObj)
63 {
64     if (!arrayObj->HasStableElements(thread)) {
65         return std::make_pair(nullptr, 0);
66     }
67     if (arrayObj->IsStableJSArguments(thread)) {
68         JSHandle<JSArguments> argList = JSHandle<JSArguments>::Cast(arrayObj);
69         TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
70         auto env = thread->GetEcmaVM()->GetGlobalEnv();
71         if (argList->GetClass() != env->GetArgumentsClass().GetObject<JSHClass>()) {
72             return std::make_pair(nullptr, 0);
73         }
74         auto result = argList->GetPropertyInlinedProps(JSArguments::LENGTH_INLINE_PROPERTY_INDEX);
75         if (!result.IsInt()) {
76             return std::make_pair(nullptr, 0);
77         }
78         size_t length = static_cast<size_t>(result.GetInt());
79         size_t res = MakeArgListWithHole(thread, elements, length);
80         return std::make_pair(elements, res);
81     } else if (arrayObj->IsStableJSArray(thread)) {
82         JSHandle<JSArray> argList = JSHandle<JSArray>::Cast(arrayObj);
83         TaggedArray *elements = nullptr;
84         if (argList->GetElements().IsMutantTaggedArray()) {
85             JSHandle<JSObject> obj(arrayObj);
86             int elementsLength = static_cast<int>(ElementAccessor::GetElementsLength(obj));
87             JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->
88                                                 NewTaggedArray(elementsLength, JSTaggedValue::Undefined());
89             for (int i = 0; i < elementsLength; ++i) {
90                 JSTaggedValue value = ElementAccessor::Get(obj, i);
91                 newElements->Set(thread, i, value);
92             }
93             elements = *newElements;
94         } else {
95             elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
96         }
97         size_t length = argList->GetArrayLength();
98         if (elements->GetLength() == 0 && length != 0) {
99             JSHandle<TaggedArray> array =
100                 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined());
101             return std::make_pair(*array, length);
102         }
103         size_t res = MakeArgListWithHole(thread, elements, length);
104         return std::make_pair(elements, res);
105     } else {
106         LOG_ECMA(FATAL) << "this branch is unreachable";
107         UNREACHABLE();
108     }
109 }
110 }  // anonymous namespace
111 
112 // ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray)
FunctionPrototypeApply(EcmaRuntimeCallInfo * argv)113 JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv)
114 {
115     ASSERT(argv);
116     JSThread *thread = argv->GetThread();
117     BUILTINS_API_TRACE(thread, Function, PrototypeApply);
118     [[maybe_unused]] EcmaHandleScope handleScope(thread);
119 
120     // 1. If IsCallable(func) is false, throw a TypeError exception.
121     if (!GetThis(argv)->IsCallable()) {
122         THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception());
123     }
124 
125     JSHandle<JSTaggedValue> func = GetThis(argv);
126     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
127     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
128     // 2. If argArray is null or undefined, then
129     if (GetCallArg(argv, 1)->IsUndefined()) {  // null will also get undefined
130         // a. Return Call(func, thisArg).
131         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, 0);
132         return JSFunction::Call(info);
133     }
134     // 3. Let argList be CreateListFromArrayLike(argArray).
135     JSHandle<JSTaggedValue> arrayObj = GetCallArg(argv, 1);
136     std::pair<TaggedArray*, size_t> argumentsList = BuildArgumentsListFast(thread, arrayObj);
137     if (!argumentsList.first) {
138         JSHandle<JSTaggedValue> num = JSObject::CreateListFromArrayLike(thread, arrayObj);
139         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
140         JSHandle<TaggedArray> argList = JSHandle<TaggedArray>::Cast(num);
141         // 4. ReturnIfAbrupt(argList).
142         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
143         const uint32_t argsLength = argList->GetLength();
144         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
145         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
146         info->SetCallArg(argsLength, argList);
147         return JSFunction::Call(info);
148     }
149     // 6. Return Call(func, thisArg, argList).
150     const uint32_t argsLength = static_cast<uint32_t>(argumentsList.second);
151     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
152     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
153     info->SetCallArg(argsLength, argumentsList.first);
154     return JSFunction::Call(info);
155 }
156 
157 // ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args)
FunctionPrototypeBind(EcmaRuntimeCallInfo * argv)158 JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv)
159 {
160     ASSERT(argv);
161     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind);
162     JSThread *thread = argv->GetThread();
163     [[maybe_unused]] EcmaHandleScope handleScope(thread);
164 
165     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
166     // 1. Let Target be the this value.
167     JSHandle<JSTaggedValue> target = GetThis(argv);
168     // 2. If IsCallable(Target) is false, throw a TypeError exception.
169     if (!target->IsCallable()) {
170         THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception());
171     }
172 
173     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
174     uint32_t argsLength = 0;
175     if (argv->GetArgsNumber() > 1) {
176         argsLength = argv->GetArgsNumber() - 1;
177     }
178 
179     // 3. Let args be a new (possibly empty) List consisting of all of the argument
180     //    values provided after thisArg in order.
181     JSHandle<TaggedArray> argsArray = factory->NewTaggedArray(argsLength);
182     for (uint32_t index = 0; index < argsLength; ++index) {
183         argsArray->Set(thread, index, GetCallArg(argv, index + 1));
184     }
185     // 4. Let F be BoundFunctionCreate(Target, thisArg, args).
186     JSHandle<JSBoundFunction> boundFunction = factory->NewJSBoundFunction(target, thisArg, argsArray);
187     // 5. ReturnIfAbrupt(F)
188     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
189 
190     auto globalConst = thread->GlobalConstants();
191     JSTaggedValue functionLengthAccessor = globalConst->GetFunctionLengthAccessor();
192     JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
193     JSTaggedValue lengthProperty;
194     if (target->IsBoundFunction() || target->IsJSFunction()) { // fastpath
195         JSHandle<JSObject> obj(thread, target.GetTaggedValue());
196         uint32_t numberOfInlinedProps = obj->GetJSHClass()->GetInlinedProperties();
197         if (JSFunction::LENGTH_INLINE_PROPERTY_INDEX < numberOfInlinedProps) {
198             lengthProperty = obj->GetPropertyInlinedProps(JSFunction::LENGTH_INLINE_PROPERTY_INDEX);
199         }
200     }
201     if (lengthProperty.IsHole()) {
202         lengthProperty = ObjectFastOperator::FastGetPropertyByName(
203             thread, target.GetTaggedValue(), lengthKey.GetTaggedValue());
204     }
205 
206     if (!lengthProperty.IsAccessor() || functionLengthAccessor != lengthProperty) {
207         // 6. Let targetHasLength be HasOwnProperty(Target, "length").
208         bool targetHasLength = JSTaggedValue::HasOwnProperty(thread, target, lengthKey);
209         // 7. ReturnIfAbrupt(targetHasLength).
210         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
211 
212         double lengthValue = 0.0;
213         // 8. If targetHasLength is true, then
214         if (targetHasLength) {
215             // a. Let targetLen be Get(Target, "length").
216             JSHandle<JSTaggedValue> targetLen = JSTaggedValue::GetProperty(thread, target, lengthKey).GetValue();
217             // b. ReturnIfAbrupt(targetLen).
218             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
219 
220             // c. If Type(targetLen) is not Number, let L be 0.
221             // d. Else,
222             //    i. Let targetLen be ToInteger(targetLen).
223             //    ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
224             if (targetLen->IsNumber()) {
225                 // argv include thisArg
226                 lengthValue =
227                     std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() -
228                              static_cast<double>(argsLength));
229                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
230             }
231         }
232         // 9. Else let L be 0.
233 
234         // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L,
235         //     [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
236         PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(lengthValue)),
237                                 false, false, true);
238         [[maybe_unused]] bool status =
239             JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(boundFunction), lengthKey, desc);
240         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
241         // 11. Assert: status is not an abrupt completion.
242         ASSERT_PRINT(status, "DefinePropertyOrThrow failed");
243     }
244 
245     JSTaggedValue functionNameAccessor = globalConst->GetFunctionNameAccessor();
246     JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
247     JSTaggedValue nameProperty;
248     if (target->IsBoundFunction() || target->IsJSFunction()) { // fastpath
249         JSHandle<JSObject> obj(thread, target.GetTaggedValue());
250         uint32_t numberOfInlinedProps = obj->GetJSHClass()->GetInlinedProperties();
251         if (JSFunction::NAME_INLINE_PROPERTY_INDEX < numberOfInlinedProps) {
252             nameProperty = obj->GetPropertyInlinedProps(JSFunction::NAME_INLINE_PROPERTY_INDEX);
253         }
254     }
255     if (nameProperty.IsHole()) {
256         nameProperty = ObjectFastOperator::FastGetPropertyByName(
257             thread, target.GetTaggedValue(), nameKey.GetTaggedValue());
258     }
259 
260     if (!nameProperty.IsAccessor() || functionNameAccessor != nameProperty) {
261         // 12. Let targetName be Get(Target, "name").
262         JSHandle<JSTaggedValue> targetName = JSObject::GetProperty(thread, target, nameKey).GetValue();
263         // 13. ReturnIfAbrupt(targetName).
264         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
265 
266         JSHandle<JSTaggedValue> boundName = thread->GlobalConstants()->GetHandledBoundString();
267         // 14. If Type(targetName) is not String, let targetName be the empty string.
268         // 15. Perform SetFunctionName(F, targetName, "bound").
269         [[maybe_unused]] bool status;
270         if (!targetName->IsString()) {
271             JSHandle<JSTaggedValue> emptyString(factory->GetEmptyString());
272             status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction),
273                                                  emptyString, boundName);
274         } else {
275             status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction),
276                                                  targetName, boundName);
277         }
278         // Assert: status is not an abrupt completion.
279         ASSERT_PRINT(status, "DefinePropertyOr failed");
280     }
281 
282     // 16. Return F.
283     return boundFunction.GetTaggedValue();
284 }
285 
286 // ecma 19.2.3.3 Function.prototype.call (thisArg , ...args)
FunctionPrototypeCall(EcmaRuntimeCallInfo * argv)287 JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv)
288 {
289     ASSERT(argv);
290     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall);
291     JSThread *thread = argv->GetThread();
292     [[maybe_unused]] EcmaHandleScope handleScope(thread);
293 
294     // 1. If IsCallable(func) is false, throw a TypeError exception.
295     if (!GetThis(argv)->IsCallable()) {
296         THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception());
297     }
298 
299     JSHandle<JSTaggedValue> func = GetThis(argv);
300     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
301     uint32_t argsLength = 0;
302     if (argv->GetArgsNumber() > 1) {
303         argsLength = argv->GetArgsNumber() - 1;
304     }
305     // 2. Let argList be an empty List.
306     // 3. If this method was called with more than one argument then in left to right order,
307     //    starting with the second argument, append each argument as the last element of argList.
308     // 5. Return Call(func, thisArg, argList).
309     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
310     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
311     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
312     info->SetCallArg(argsLength, 0, argv, 1);
313     return JSFunction::Call(info);
314 }
315 
316 // ecma 19.2.3.5 Function.prototype.toString ()
FunctionPrototypeToString(EcmaRuntimeCallInfo * argv)317 JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv)
318 {
319     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString);
320     JSThread *thread = argv->GetThread();
321     [[maybe_unused]] EcmaHandleScope handleScope(thread);
322     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
323     if (thisValue->IsJSObject() && thisValue->IsCallable()) {
324         JSHandle<Method> method;
325         if (thisValue->IsBoundFunction()) {
326             JSHandle<JSBoundFunction> func = JSHandle<JSBoundFunction>::Cast(thisValue);
327             JSHandle<JSTaggedValue> methodHandle(thread, func->GetMethod());
328             method = JSHandle<Method>::Cast(methodHandle);
329         } else {
330             JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(thisValue);
331             JSHandle<JSTaggedValue> methodHandle(thread, func->GetMethod());
332             method = JSHandle<Method>::Cast(methodHandle);
333         }
334         if (method->IsNativeWithCallField()) {
335             JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
336             JSHandle<EcmaString> methodName(JSObject::GetProperty(thread, thisValue, nameKey).GetValue());
337             std::string nameStr = EcmaStringAccessor(methodName).ToStdString();
338             std::string startStr = "function ";
339             std::string endStr = "() { [native code] }";
340             startStr.append(nameStr).append(endStr);
341             return GetTaggedString(thread, startStr.c_str());
342         }
343         DebugInfoExtractor *debugExtractor =
344                 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
345         const std::string &sourceCode = debugExtractor->GetSourceCode(method->GetMethodId());
346         if (!sourceCode.empty()) {
347             return GetTaggedString(thread, sourceCode.c_str());
348         } else {
349             return GetTaggedString(thread, "Cannot get source code of funtion");
350         }
351     }
352 
353     THROW_TYPE_ERROR_AND_RETURN(thread,
354         "function.toString() target is incompatible object", JSTaggedValue::Exception());
355 }
356 
357 // ecma 19.2.3.6 Function.prototype[@@hasInstance] (V)
FunctionPrototypeHasInstance(EcmaRuntimeCallInfo * argv)358 JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv)
359 {
360     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance);
361     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
362     // 1. Let F be the this value.
363     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
364     // 2. Return OrdinaryHasInstance(F, V).
365     JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
366     return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true)
367                                                                               : GetTaggedBoolean(false);
368 }
369 }  // namespace panda::ecmascript::builtins
370