KJS

function_object.cpp
1 // krazy:excludeall=doublequote_chars (UStrings aren't QStrings)
2 /*
3  * This file is part of the KDE libraries
4  * Copyright (C) 1999-2001 Harri Porten ([email protected])
5  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  */
22 
23 #include "function_object.h"
24 #include "internal.h"
25 #include "scriptfunction.h"
26 #include "array_object.h"
27 #include "nodes.h"
28 #include "lexer.h"
29 #include "debugger.h"
30 #include "object.h"
31 
32 #include <assert.h>
33 #include <stdio.h>
34 #include <string.h>
35 
36 using namespace KJS;
37 
38 // ------------------------------ FunctionPrototype -------------------------
39 
40 FunctionPrototype::FunctionPrototype(ExecState *exec)
41 {
42  static const Identifier *applyPropertyName = new Identifier("apply");
43  static const Identifier *callPropertyName = new Identifier("call");
44  static const Identifier *bindPropertyName = new Identifier("bind");
45 
46  putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
47  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::ToString, 0, exec->propertyNames().toString), DontEnum);
48  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Apply, 2, *applyPropertyName), DontEnum);
49  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Call, 1, *callPropertyName), DontEnum);
50  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Bind, 1, *bindPropertyName), DontEnum);
51 }
52 
53 FunctionPrototype::~FunctionPrototype()
54 {
55 }
56 
57 // ECMA 15.3.4
58 JSValue *FunctionPrototype::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
59 {
60  return jsUndefined();
61 }
62 
63 // ------------------------------ FunctionProtoFunc -------------------------
64 
65 FunctionProtoFunc::FunctionProtoFunc(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name)
66  : InternalFunctionImp(funcProto, name)
67  , id(i)
68 {
69  putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
70 }
71 
72 JSValue *FunctionProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
73 {
74  JSValue *result = nullptr;
75 
76  switch (id) {
77  case ToString:
78  if (!thisObj || !thisObj->inherits(&InternalFunctionImp::info)) {
79 #ifndef NDEBUG
80  fprintf(stderr, "attempted toString() call on null or non-function object\n");
81 #endif
82  return throwError(exec, TypeError);
83  }
84  if (thisObj->inherits(&FunctionImp::info)) {
85  return jsString(static_cast<FunctionImp *>(thisObj)->toSource());
86  } else if (thisObj->inherits(&InternalFunctionImp::info) &&
87  !static_cast<InternalFunctionImp *>(thisObj)->functionName().isNull()) {
88  result = jsString("\nfunction " + static_cast<InternalFunctionImp *>(thisObj)->functionName().ustring() + "() {\n"
89  " [native code]\n}\n");
90  } else {
91  result = jsString("[function]");
92  }
93  break;
94  case Apply: {
95  JSValue *thisArg = args[0];
96  JSValue *argArray = args[1];
97  JSObject *func = thisObj;
98 
99  if (!func->implementsCall()) {
100  return throwError(exec, TypeError);
101  }
102 
103  JSObject *applyThis;
104  if (JSValue::isUndefinedOrNull(thisArg)) {
105  applyThis = exec->dynamicInterpreter()->globalObject();
106  } else {
107  applyThis = JSValue::toObject(thisArg, exec);
108  }
109 
110  List applyArgs;
111  if (!JSValue::isUndefinedOrNull(argArray)) {
112  if (JSValue::isObject(argArray) &&
113  (static_cast<JSObject *>(argArray)->inherits(&ArrayInstance::info) ||
114  static_cast<JSObject *>(argArray)->inherits(&Arguments::info))) {
115 
116  JSObject *argArrayObj = static_cast<JSObject *>(argArray);
117  unsigned int length = JSValue::toUInt32(argArrayObj->get(exec, exec->propertyNames().length), exec);
118  for (unsigned int i = 0; i < length; i++) {
119  applyArgs.append(argArrayObj->get(exec, i));
120  }
121  } else {
122  return throwError(exec, TypeError);
123  }
124  }
125  result = func->call(exec, applyThis, applyArgs);
126  }
127  break;
128  case Call: {
129  JSValue *thisArg = args[0];
130  JSObject *func = thisObj;
131 
132  if (!func->implementsCall()) {
133  return throwError(exec, TypeError);
134  }
135 
136  JSObject *callThis;
137  if (JSValue::isUndefinedOrNull(thisArg)) {
138  callThis = exec->dynamicInterpreter()->globalObject();
139  } else {
140  callThis = JSValue::toObject(thisArg, exec);
141  }
142 
143  result = func->call(exec, callThis, args.copyTail());
144  }
145  break;
146  case Bind: { //ECMA Edition 5.1r6 - 15.3.4.5
147  JSObject *target(thisObj);
148  if (!target->implementsCall()) {
149  return throwError(exec, TypeError, "object is not callable");
150  }
151 
152  List newArgs;
153  for (int i = 1; i < args.size(); ++i) {
154  newArgs.append(args[i]);
155  }
156 
157  JSObject *boundThis = nullptr;
158 
159  // As call does not accept JSValue(undefined/null),
160  // do it like in call and use the global object
161  if (JSValue::isUndefinedOrNull(args[0])) {
162  boundThis = exec->dynamicInterpreter()->globalObject();
163  } else {
164  boundThis = JSValue::toObject(args[0], exec);
165  }
166 
167  BoundFunction *bfunc = new BoundFunction(exec, target, boundThis, newArgs);
168 
169  unsigned length;
170  if (target->inherits(&FunctionImp::info)) {
171  double L = JSValue::getNumber(target->get(exec, exec->propertyNames().length)) - newArgs.size();
172  length = (unsigned)std::max<int>((int)L, 0);
173  } else {
174  length = 0;
175  }
176  bfunc->put(exec, exec->propertyNames().length, jsNumber(length), ReadOnly | DontEnum | DontDelete);
177 
178  JSObject *thrower = new Thrower(TypeError);
179  PropertyDescriptor callerDesc;
180 
181  GetterSetterImp *gs = new GetterSetterImp();
182  gs->setGetter(thrower);
183  gs->setSetter(thrower);
184 
185  callerDesc.setPropertyDescriptorValues(exec, gs, DontEnum | DontDelete);
186  bfunc->defineOwnProperty(exec, exec->propertyNames().caller, callerDesc, false);
187 
188  PropertyDescriptor argumentsDesc;
189  argumentsDesc.setPropertyDescriptorValues(exec, gs, DontEnum | DontDelete);
190  bfunc->defineOwnProperty(exec, exec->propertyNames().arguments, argumentsDesc, false);
191 
192  return bfunc;
193  }
194  break;
195  }
196 
197  return result;
198 }
199 
200 // ------------------------------ FunctionObjectImp ----------------------------
201 
202 FunctionObjectImp::FunctionObjectImp(ExecState *exec, FunctionPrototype *funcProto)
203  : InternalFunctionImp(funcProto)
204 {
205  putDirect(exec->propertyNames().prototype, funcProto, DontEnum | DontDelete | ReadOnly);
206 
207  // no. of arguments for constructor
208  putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum);
209 }
210 
211 FunctionObjectImp::~FunctionObjectImp()
212 {
213 }
214 
215 bool FunctionObjectImp::implementsConstruct() const
216 {
217  return true;
218 }
219 
220 // ECMA 15.3.2 The Function Constructor
221 JSObject *FunctionObjectImp::construct(ExecState *exec, const List &args, const Identifier &functionName, const UString &sourceURL, int lineNumber)
222 {
223  UString p("");
224  UString body;
225  int argsSize = args.size();
226  if (argsSize == 0) {
227  body = "";
228  } else if (argsSize == 1) {
229  body = JSValue::toString(args[0], exec);
230  } else {
231  p = JSValue::toString(args[0], exec);
232  for (int k = 1; k < argsSize - 1; k++) {
233  p += "," + JSValue::toString(args[k], exec);
234  }
235  body = JSValue::toString(args[argsSize - 1], exec);
236  }
237 
238  // parse the source code
239  int sourceId;
240  int errLine;
241  UString errMsg;
242  RefPtr<FunctionBodyNode> functionBody = parser().parseFunctionBody(sourceURL, lineNumber, body.data(), body.size(), &sourceId, &errLine, &errMsg);
243 
244  // notify debugger that source has been parsed
245  Debugger *dbg = exec->dynamicInterpreter()->debugger();
246  if (dbg) {
247  // make sure to pass in sourceURL, since it's useful for lazy event listeners, and empty for actual function ctor
248  dbg->reportSourceParsed(exec, functionBody.get(), sourceId, sourceURL, body, lineNumber, errLine, errMsg);
249  }
250 
251  // no program node == syntax error - throw a syntax error
252  if (!functionBody)
253  // we can't return a Completion(Throw) here, so just set the exception
254  // and return it
255  {
256  return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL);
257  }
258 
259  ScopeChain scopeChain;
260  scopeChain.push(exec->lexicalInterpreter()->globalObject());
261 
262  FunctionImp *fimp = new FunctionImp(exec, functionName, functionBody.get(), scopeChain);
263 
264  // parse parameter list. throw syntax error on illegal identifiers
265  int len = p.size();
266  const UChar *c = p.data();
267  int i = 0, params = 0;
268  UString param;
269  while (i < len) {
270  while (*c == ' ' && i < len) {
271  c++, i++;
272  }
273  if (Lexer::isIdentStart(c->uc)) { // else error
274  param = UString(c, 1);
275  c++, i++;
276  while (i < len && (Lexer::isIdentPart(c->uc))) {
277  param += UString(c, 1);
278  c++, i++;
279  }
280  while (i < len && *c == ' ') {
281  c++, i++;
282  }
283  if (i == len) {
284  functionBody->addParam(Identifier(param));
285  params++;
286  break;
287  } else if (*c == ',') {
288  functionBody->addParam(Identifier(param));
289  params++;
290  c++, i++;
291  continue;
292  } // else error
293  }
294  return throwError(exec, SyntaxError, "Syntax error in parameter list");
295  }
296 
297  List consArgs;
298 
299  JSObject *objCons = exec->lexicalInterpreter()->builtinObject();
300  JSObject *prototype = objCons->construct(exec, List::empty());
301  prototype->put(exec, exec->propertyNames().constructor, fimp, DontEnum | DontDelete | ReadOnly);
302  // ECMA Edition 5.1r6 - 15.3.5.2 - [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false
303  fimp->put(exec, exec->propertyNames().prototype, prototype, Internal | DontDelete | DontEnum);
304  return fimp;
305 }
306 
307 // ECMA 15.3.2 The Function Constructor
308 JSObject *FunctionObjectImp::construct(ExecState *exec, const List &args)
309 {
310  return construct(exec, args, "anonymous", UString(), 0);
311 }
312 
313 // ECMA 15.3.1 The Function Constructor Called as a Function
314 JSValue *FunctionObjectImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
315 {
316  return construct(exec, args);
317 }
Interpreter * dynamicInterpreter() const
Returns the interpreter associated with this execution state.
Definition: ExecState.h:64
int size() const
Definition: ustring.h:420
void append(JSValue *val)
Append an object to the end of the list.
Definition: list.h:186
A minimal object that just throws an exception if executed.
Definition: function.h:73
JSObject * builtinObject() const
Returns the builtin "Object" object.
List copyTail() const
Make a copy of the list, omitting the first element.
Definition: list.cpp:281
Implementation class for internal Functions.
Unicode string class.
Definition: ustring.h:153
Unicode character.
Definition: ustring.h:70
JSValue is the base type for all primitives (Undefined, Null, Boolean, String, Number) and objects in...
Definition: value.h:58
int size() const
Definition: list.h:112
static const List & empty()
Returns a pointer to a static instance of an empty list.
Definition: list.cpp:311
Native list type.
Definition: list.h:52
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope&#39;s global object.
Definition: ExecState.cpp:35
Represents the current state of script execution.
Definition: ExecState.h:53
Represents an Identifier for a Javascript object.
Definition: identifier.h:36
const UChar * data() const
Definition: ustring.h:391
JSGlobalObject * globalObject() const
Returns the object that is used as the global object during all script execution performed by this in...
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jan 20 2022 22:52:43 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.