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 }
void append(JSValue *val)
Append an object to the end of the list.
Definition: list.h:186
JSGlobalObject * globalObject() const
Returns the object that is used as the global object during all script execution performed by this in...
JSValue is the base type for all primitives (Undefined, Null, Boolean, String, Number) and objects in...
Definition: value.h:58
int size() const
Definition: ustring.h:420
int size() const
Definition: list.h:112
JSObject * builtinObject() const
Returns the builtin "Object" object.
Represents the current state of script execution.
Definition: ExecState.h:53
Represents an Identifier for a Javascript object.
Definition: identifier.h:36
Unicode character.
Definition: ustring.h:70
List copyTail() const
Make a copy of the list, omitting the first element.
Definition: list.cpp:281
const UChar * data() const
Definition: ustring.h:391
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Definition: ExecState.cpp:35
A minimal object that just throws an exception if executed.
Definition: function.h:73
Native list type.
Definition: list.h:52
const char * name(StandardAction id)
static const List & empty()
Returns a pointer to a static instance of an empty list.
Definition: list.cpp:311
Implementation class for internal Functions.
Interpreter * dynamicInterpreter() const
Returns the interpreter associated with this execution state.
Definition: ExecState.h:64
Unicode string class.
Definition: ustring.h:153
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Mar 27 2023 04:10:38 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.