• 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/tagged_array-inl.h"
25 
26 namespace panda::ecmascript::builtins {
27 // ecma 19.2.1 Function (p1, p2, ... , pn, body)
FunctionConstructor(EcmaRuntimeCallInfo * argv)28 JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv)
29 {
30     // not support
31     JSThread *thread = argv->GetThread();
32     BUILTINS_API_TRACE(thread, Function, Constructor);
33     [[maybe_unused]] EcmaHandleScope handleScope(thread);
34     THROW_TYPE_ERROR_AND_RETURN(thread, "Not support eval. Forbidden using new Function()/Function().",
35                                 JSTaggedValue::Exception());
36 }
37 
38 // ecma 19.2.3 The Function prototype object is itself a built-in function object.
39 //             When invoked, it accepts any arguments and returns undefined.
FunctionPrototypeInvokeSelf(EcmaRuntimeCallInfo * argv)40 JSTaggedValue BuiltinsFunction::FunctionPrototypeInvokeSelf([[maybe_unused]] EcmaRuntimeCallInfo *argv)
41 {
42     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeInvokeSelf);
43     return JSTaggedValue::Undefined();
44 }
45 namespace {
MakeArgListWithHole(JSThread * thread,TaggedArray * argv,int length)46 static size_t MakeArgListWithHole(JSThread *thread, TaggedArray *argv, int length)
47 {
48     if (length <= 0) {
49         return 0;
50     }
51     size_t newlength = static_cast<size_t>(length);
52     size_t arryLength = argv->GetLength();
53     if (newlength > arryLength) {
54         length = arryLength;
55     }
56     for (size_t index = 0; index < newlength; ++index) {
57         JSTaggedValue value = argv->Get(thread, index);
58         if (value.IsHole()) {
59             argv->Set(thread, index, JSTaggedValue::Undefined());
60         }
61     }
62     return length;
63 }
64 
BuildArgumentsListFast(JSThread * thread,const JSHandle<JSTaggedValue> & arrayObj)65 static std::pair<TaggedArray*, size_t> BuildArgumentsListFast(JSThread *thread,
66                                                               const JSHandle<JSTaggedValue> &arrayObj)
67 {
68     if (!arrayObj->HasStableElements(thread)) {
69         return std::make_pair(nullptr, 0);
70     }
71     if (arrayObj->IsStableJSArguments(thread)) {
72         JSHandle<JSArguments> argList = JSHandle<JSArguments>::Cast(arrayObj);
73         TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
74         auto env = thread->GetEcmaVM()->GetGlobalEnv();
75         if (argList->GetClass() != env->GetArgumentsClass().GetObject<JSHClass>()) {
76             return std::make_pair(nullptr, 0);
77         }
78         auto result = argList->GetPropertyInlinedProps(JSArguments::LENGTH_INLINE_PROPERTY_INDEX);
79         if (!result.IsInt()) {
80             return std::make_pair(nullptr, 0);
81         }
82         auto length = result.GetInt();
83         size_t res = MakeArgListWithHole(thread, elements, length);
84         return std::make_pair(elements, res);
85     } else if (arrayObj->IsStableJSArray(thread)) {
86         JSHandle<JSArray> argList = JSHandle<JSArray>::Cast(arrayObj);
87         TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
88         size_t length = argList->GetArrayLength();
89         if (elements->GetLength() == 0 && length != 0) {
90             JSHandle<TaggedArray> array =
91                 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined());
92             return std::make_pair(*array, length);
93         }
94         size_t res = MakeArgListWithHole(thread, elements, length);
95         return std::make_pair(elements, res);
96     } else {
97         LOG_ECMA(FATAL) << "this branch is unreachable";
98         UNREACHABLE();
99     }
100 }
101 }  // anonymous namespace
102 
103 // ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray)
FunctionPrototypeApply(EcmaRuntimeCallInfo * argv)104 JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv)
105 {
106     ASSERT(argv);
107     JSThread *thread = argv->GetThread();
108     BUILTINS_API_TRACE(thread, Function, PrototypeApply);
109     [[maybe_unused]] EcmaHandleScope handleScope(thread);
110 
111     // 1. If IsCallable(func) is false, throw a TypeError exception.
112     if (!GetThis(argv)->IsCallable()) {
113         THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception());
114     }
115 
116     JSHandle<JSTaggedValue> func = GetThis(argv);
117     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
118     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
119     // 2. If argArray is null or undefined, then
120     if (GetCallArg(argv, 1)->IsUndefined()) {  // null will also get undefined
121         // a. Return Call(func, thisArg).
122         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, 0);
123         return JSFunction::Call(info);
124     }
125     // 3. Let argList be CreateListFromArrayLike(argArray).
126     JSHandle<JSTaggedValue> arrayObj = GetCallArg(argv, 1);
127     std::pair<TaggedArray*, size_t> argumentsList = BuildArgumentsListFast(thread, arrayObj);
128     if (!argumentsList.first) {
129         JSHandle<JSTaggedValue> num = JSObject::CreateListFromArrayLike(thread, arrayObj);
130         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131         JSHandle<TaggedArray> argList = JSHandle<TaggedArray>::Cast(num);
132         // 4. ReturnIfAbrupt(argList).
133         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
134         const uint32_t argsLength = argList->GetLength();
135         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
136         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
137         info->SetCallArg(argsLength, argList);
138         return JSFunction::Call(info);
139     }
140     // 6. Return Call(func, thisArg, argList).
141     const uint32_t argsLength = static_cast<uint32_t>(argumentsList.second);
142     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
143     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
144     info->SetCallArg(argsLength, argumentsList.first);
145     return JSFunction::Call(info);
146 }
147 
148 // ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args)
FunctionPrototypeBind(EcmaRuntimeCallInfo * argv)149 JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv)
150 {
151     ASSERT(argv);
152     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind);
153     JSThread *thread = argv->GetThread();
154     [[maybe_unused]] EcmaHandleScope handleScope(thread);
155 
156     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
157     // 1. Let Target be the this value.
158     JSHandle<JSTaggedValue> target = GetThis(argv);
159     // 2. If IsCallable(Target) is false, throw a TypeError exception.
160     if (!target->IsCallable()) {
161         THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception());
162     }
163 
164     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
165     uint32_t argsLength = 0;
166     if (argv->GetArgsNumber() > 1) {
167         argsLength = argv->GetArgsNumber() - 1;
168     }
169 
170     // 3. Let args be a new (possibly empty) List consisting of all of the argument
171     //    values provided after thisArg in order.
172     JSHandle<TaggedArray> argsArray = factory->NewTaggedArray(argsLength);
173     for (uint32_t index = 0; index < argsLength; ++index) {
174         argsArray->Set(thread, index, GetCallArg(argv, index + 1));
175     }
176     // 4. Let F be BoundFunctionCreate(Target, thisArg, args).
177     JSHandle<JSBoundFunction> boundFunction = factory->NewJSBoundFunction(target, thisArg, argsArray);
178     // 5. ReturnIfAbrupt(F)
179     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
180 
181     // 6. Let targetHasLength be HasOwnProperty(Target, "length").
182     auto globalConst = thread->GlobalConstants();
183     JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
184     bool targetHasLength = JSTaggedValue::HasOwnProperty(thread, target, lengthKey);
185     // 7. ReturnIfAbrupt(targetHasLength).
186     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
187 
188     double lengthValue = 0.0;
189     // 8. If targetHasLength is true, then
190     if (targetHasLength) {
191         // a. Let targetLen be Get(Target, "length").
192         JSHandle<JSTaggedValue> targetLen = JSTaggedValue::GetProperty(thread, target, lengthKey).GetValue();
193         // b. ReturnIfAbrupt(targetLen).
194         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
195 
196         // c. If Type(targetLen) is not Number, let L be 0.
197         // d. Else,
198         //    i. Let targetLen be ToInteger(targetLen).
199         //    ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
200         if (targetLen->IsNumber()) {
201             // argv include thisArg
202             lengthValue =
203                 std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() - static_cast<double>(argsLength));
204         }
205     }
206     // 9. Else let L be 0.
207 
208     // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L,
209     //     [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
210     PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(lengthValue)), false, false, true);
211     [[maybe_unused]] bool status =
212         JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(boundFunction), lengthKey, desc);
213     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
214     // 11. Assert: status is not an abrupt completion.
215     ASSERT_PRINT(status, "DefinePropertyOrThrow failed");
216 
217     // 12. Let targetName be Get(Target, "name").
218     JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
219     JSHandle<JSTaggedValue> targetName = JSObject::GetProperty(thread, target, nameKey).GetValue();
220     // 13. ReturnIfAbrupt(targetName).
221     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
222 
223     JSHandle<JSTaggedValue> boundName = thread->GlobalConstants()->GetHandledBoundString();
224     // 14. If Type(targetName) is not String, let targetName be the empty string.
225     // 15. Perform SetFunctionName(F, targetName, "bound").
226     if (!targetName->IsString()) {
227         JSHandle<JSTaggedValue> emptyString(factory->GetEmptyString());
228         status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), emptyString, boundName);
229     } else {
230         status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), targetName, boundName);
231     }
232     // Assert: status is not an abrupt completion.
233     ASSERT_PRINT(status, "SetFunctionName failed");
234 
235     // 16. Return F.
236     return boundFunction.GetTaggedValue();
237 }
238 
239 // ecma 19.2.3.3 Function.prototype.call (thisArg , ...args)
FunctionPrototypeCall(EcmaRuntimeCallInfo * argv)240 JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv)
241 {
242     ASSERT(argv);
243     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall);
244     JSThread *thread = argv->GetThread();
245     [[maybe_unused]] EcmaHandleScope handleScope(thread);
246 
247     // 1. If IsCallable(func) is false, throw a TypeError exception.
248     if (!GetThis(argv)->IsCallable()) {
249         THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception());
250     }
251 
252     JSHandle<JSTaggedValue> func = GetThis(argv);
253     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
254     uint32_t argsLength = 0;
255     if (argv->GetArgsNumber() > 1) {
256         argsLength = argv->GetArgsNumber() - 1;
257     }
258     // 2. Let argList be an empty List.
259     // 3. If this method was called with more than one argument then in left to right order,
260     //    starting with the second argument, append each argument as the last element of argList.
261     // 5. Return Call(func, thisArg, argList).
262     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
263     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
264     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
265     info->SetCallArg(argsLength, 0, argv, 1);
266     return JSFunction::Call(info);
267 }
268 
269 // ecma 19.2.3.5 Function.prototype.toString ()
FunctionPrototypeToString(EcmaRuntimeCallInfo * argv)270 JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv)
271 {
272     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString);
273     JSThread *thread = argv->GetThread();
274     [[maybe_unused]] EcmaHandleScope handleScope(thread);
275     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
276     if (thisValue->IsJSObject() && thisValue->IsCallable()) {
277         JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(thisValue);
278         JSHandle<Method> method(thread, func->GetMethod());
279         if (method->IsNativeWithCallField()) {
280             JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
281             JSHandle<EcmaString> methodName(JSObject::GetProperty(thread, thisValue, nameKey).GetValue());
282             std::string nameStr = EcmaStringAccessor(methodName).ToStdString();
283             std::string startStr = "function ";
284             std::string endStr = "() { [native code] }";
285             startStr.append(nameStr).append(endStr);
286             return GetTaggedString(thread, startStr.c_str());
287         }
288         DebugInfoExtractor *debugExtractor =
289                 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
290         const std::string &sourceCode = debugExtractor->GetSourceCode(method->GetMethodId());
291         if (!sourceCode.empty()) {
292             return GetTaggedString(thread, sourceCode.c_str());
293         } else {
294             return GetTaggedString(thread, "Cannot get source code of funtion");
295         }
296     }
297 
298     THROW_TYPE_ERROR_AND_RETURN(thread,
299         "function.toString() target is incompatible object", JSTaggedValue::Exception());
300 }
301 
302 // ecma 19.2.3.6 Function.prototype[@@hasInstance] (V)
FunctionPrototypeHasInstance(EcmaRuntimeCallInfo * argv)303 JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv)
304 {
305     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance);
306     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
307     // 1. Let F be the this value.
308     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
309     // 2. Return OrdinaryHasInstance(F, V).
310     JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
311     return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true)
312                                                                               : GetTaggedBoolean(false);
313 }
314 }  // namespace panda::ecmascript::builtins
315