source: webkit/trunk/Source/JavaScriptCore/jsc.cpp@ 163195

Last change on this file since 163195 was 163195, checked in by [email protected], 11 years ago

Make it possible to implement JS builtins in JS
https://bugs.webkit.org/show_bug.cgi?id=127887

Reviewed by Michael Saboff.

.:

CMake updates

  • Source/cmake/gtest/CMakeLists.txt:

Source/JavaScriptCore:

This patch makes it possible to write builtin functions in JS.
The bindings, generators, and definitions are all created automatically
based on js files in the builtins/ directory. This patch includes one
such case: Array.prototype.js with an implementation of every().

There's a lot of refactoring to make it possible for CommonIdentifiers
to include the output of the generated files (DerivedSources/JSCBuiltins.{h,cpp})
without breaking the offset extractor. The result of this refactoring
is that CommonIdentifiers, and a few other miscellaneous headers now
need to be included directly as they were formerly captured through other
paths.

In addition this adds a flag to the Lookup table's hashentry to indicate
that a static function is actually backed by JS. There is then a lot of
logic to thread the special nature of the functon to where it matters.
This allows toString(), .caller, etc to mimic the behaviour of a host
function.

Notes on writing builtins:

  • Each function is compiled independently of the others, and those implementations cannot currently capture all global properties (as that could be potentially unsafe). If a function does capture a global we will deliberately crash.
  • For those "global" properties that we do want access to, we use the @ prefix, e.g. Object(this) becomes @Object(this). The @ identifiers are private names, and behave just like regular properties, only without the risk of adulteration. Again, in the @Object case, we explicitly duplicate the ObjectConstructor reference on the GlobalObject so that we have guaranteed access to the original version of the constructor.
  • call, apply, eval, and Function are all rejected identifiers, again to prevent anything from accidentally using an adulterated object. Instead @call and @apply are available, and happily they completely drop the neq_ptr instruction as they're defined as always being the original call/apply functions.

These restrictions are just intended to make it harder to accidentally
make changes that are incorrect (for instance calling whatever has been
assigned to global.Object, instead of the original constructor function).
However, making a mistake like this should result in a purely semantic
error as fundamentally these functions are treated as though they were
regular JS code in the host global, and have no more privileges than
any other JS.

The initial proof of concept is Array.prototype.every, this shows a 65%
performance improvement, and that improvement is significantly hurt by
our poor optimisation of op_in.

As this is such a limited function, we have not yet exported all symbols
that we could possibly need, but as we implement more, the likelihood
of encountering missing features will reduce.

This did require breaking out a JSStringInlines header, and required
fixing a few objects that were trying to using PropertyName::publicName
rather than PropertyName::uid.

  • API/JSCallbackObjectFunctions.h:

(JSC::JSCallbackObject<Parent>::getOwnPropertySlot):
(JSC::JSCallbackObject<Parent>::put):
(JSC::JSCallbackObject<Parent>::deleteProperty):
(JSC::JSCallbackObject<Parent>::getStaticValue):
(JSC::JSCallbackObject<Parent>::staticFunctionGetter):
(JSC::JSCallbackObject<Parent>::callbackGetter):

  • CMakeLists.txt:
  • DerivedSources.make:
  • GNUmakefile.list.am:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • builtins/Array.prototype.js:

(every):

  • builtins/BuiltinExecutables.cpp: Added.

(JSC::BuiltinExecutables::BuiltinExecutables):
(JSC::BuiltinExecutables::createBuiltinExecutable):

  • builtins/BuiltinExecutables.h:

(JSC::BuiltinExecutables::create):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::CodeBlock):

  • bytecode/CodeBlock.h:
  • bytecode/ProfiledCodeBlockJettisoningWatchpoint.cpp:
  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::generateFunctionCodeBlock):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::UnlinkedFunctionExecutable::codeBlockFor):
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):

  • bytecode/UnlinkedCodeBlock.h:

(JSC::ExecutableInfo::ExecutableInfo):
(JSC::UnlinkedFunctionExecutable::create):
(JSC::UnlinkedFunctionExecutable::toStrictness):
(JSC::UnlinkedFunctionExecutable::isBuiltinFunction):
(JSC::UnlinkedCodeBlock::isBuiltinFunction):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator):

  • bytecompiler/BytecodeGenerator.h:

(JSC::BytecodeGenerator::isBuiltinFunction):
(JSC::BytecodeGenerator::makeFunction):

  • bytecompiler/NodesCodegen.cpp:

(JSC::CallFunctionCallDotNode::emitBytecode):
(JSC::ApplyFunctionCallDotNode::emitBytecode):

  • create_hash_table:
  • dfg/DFGOperations.cpp:
  • generate-js-builtins: Added.

(getCopyright):
(getFunctions):
(generateCode):
(mangleName):
(FunctionExecutable):
(Identifier):
(JSGlobalObject):
(SourceCode):
(UnlinkedFunctionExecutable):
(VM):

  • interpreter/Interpreter.cpp:
  • interpreter/ProtoCallFrame.cpp:
  • jit/JITOpcodes.cpp:
  • jit/JITOpcodes32_64.cpp:
  • jit/JITOperations.cpp:
  • jit/JITPropertyAccess.cpp:
  • jit/JITPropertyAccess32_64.cpp:
  • jsc.cpp:
  • llint/LLIntSlowPaths.cpp:
  • parser/ASTBuilder.h:

(JSC::ASTBuilder::makeFunctionCallNode):

  • parser/Lexer.cpp:

(JSC::Lexer<T>::Lexer):
(JSC::isSafeIdentifier):
(JSC::Lexer<LChar>::parseIdentifier):
(JSC::Lexer<UChar>::parseIdentifier):
(JSC::Lexer<T>::lex):

  • parser/Lexer.h:

(JSC::isSafeIdentifier):
(JSC::Lexer<T>::lexExpectIdentifier):

  • parser/Nodes.cpp:

(JSC::ProgramNode::setClosedVariables):

  • parser/Nodes.h:

(JSC::ScopeNode::capturedVariables):
(JSC::ScopeNode::setClosedVariables):
(JSC::ProgramNode::closedVariables):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::didFinishParsing):
(JSC::Parser<LexerType>::printUnexpectedTokenText):

  • parser/Parser.h:

(JSC::Scope::getUsedVariables):
(JSC::Parser::closedVariables):
(JSC::parse):

  • parser/ParserModes.h:
  • parser/ParserTokens.h:
  • runtime/ArgList.cpp:
  • runtime/Arguments.cpp:
  • runtime/Arguments.h:
  • runtime/ArgumentsIteratorConstructor.cpp:
  • runtime/ArgumentsIteratorPrototype.cpp:
  • runtime/ArrayPrototype.cpp:
  • runtime/CodeCache.cpp:

(JSC::CodeCache::getFunctionExecutableFromGlobalCode):

  • runtime/CommonIdentifiers.cpp:

(JSC::CommonIdentifiers::CommonIdentifiers):
(JSC::CommonIdentifiers::getPrivateName):
(JSC::CommonIdentifiers::getPublicName):

  • runtime/CommonIdentifiers.h:
  • runtime/CommonSlowPaths.cpp:
  • runtime/CommonSlowPathsExceptions.cpp:
  • runtime/ExceptionHelpers.cpp:

(JSC::createUndefinedVariableError):

  • runtime/Executable.h:

(JSC::EvalExecutable::executableInfo):
(JSC::ProgramExecutable::executableInfo):
(JSC::FunctionExecutable::isBuiltinFunction):

  • runtime/FunctionPrototype.cpp:

(JSC::functionProtoFuncToString):

  • runtime/JSActivation.cpp:

(JSC::JSActivation::symbolTableGet):
(JSC::JSActivation::symbolTablePut):
(JSC::JSActivation::symbolTablePutWithAttributes):

  • runtime/JSArgumentsIterator.cpp:
  • runtime/JSArray.cpp:
  • runtime/JSArrayIterator.cpp:
  • runtime/JSCJSValue.cpp:
  • runtime/JSCellInlines.h:
  • runtime/JSFunction.cpp:

(JSC::JSFunction::createBuiltinFunction):
(JSC::JSFunction::calculatedDisplayName):
(JSC::JSFunction::sourceCode):
(JSC::JSFunction::isHostOrBuiltinFunction):
(JSC::JSFunction::isBuiltinFunction):
(JSC::JSFunction::callerGetter):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::getOwnNonIndexPropertyNames):
(JSC::JSFunction::put):
(JSC::JSFunction::defineOwnProperty):

  • runtime/JSFunction.h:
  • runtime/JSGenericTypedArrayViewConstructorInlines.h:
  • runtime/JSGenericTypedArrayViewInlines.h:
  • runtime/JSGenericTypedArrayViewPrototypeInlines.h:
  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::reset):
(JSC::JSGlobalObject::visitChildren):

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::objectConstructor):
(JSC::JSGlobalObject::symbolTableHasProperty):

  • runtime/JSObject.cpp:

(JSC::getClassPropertyNames):
(JSC::JSObject::reifyStaticFunctionsForDelete):
(JSC::JSObject::putDirectBuiltinFunction):

  • runtime/JSObject.h:
  • runtime/JSPropertyNameIterator.cpp:
  • runtime/JSPropertyNameIterator.h:
  • runtime/JSString.h:
  • runtime/JSStringInlines.h: Added.

(JSC::JSString::getStringPropertySlot):
(JSC::inlineJSValueNotStringtoString):
(JSC::JSValue::toWTFStringInline):

  • runtime/JSSymbolTableObject.cpp:

(JSC::JSSymbolTableObject::getOwnNonIndexPropertyNames):

Don't report private names.

  • runtime/JSSymbolTableObject.h:

(JSC::symbolTableGet):
(JSC::symbolTablePut):
(JSC::symbolTablePutWithAttributes):

  • runtime/Lookup.cpp:

(JSC::setUpStaticFunctionSlot):

  • runtime/Lookup.h:

(JSC::HashEntry::builtinGenerator):
(JSC::HashEntry::propertyGetter):
(JSC::HashEntry::propertyPutter):
(JSC::HashTable::entry):
(JSC::getStaticPropertySlot):
(JSC::getStaticValueSlot):
(JSC::putEntry):

  • runtime/NativeErrorConstructor.cpp:

(JSC::NativeErrorConstructor::finishCreation):

  • runtime/NativeErrorConstructor.h:
  • runtime/PropertySlot.h:
  • runtime/RegExpPrototype.cpp:
  • runtime/SetConstructor.cpp:
  • runtime/StringObject.cpp:
  • runtime/Structure.cpp:
  • runtime/VM.cpp:

(JSC::VM::VM):

  • runtime/VM.h:

(JSC::VM::builtinExecutables):

Source/WebCore:

Updating for the newly required headers.

Test: js/regress/array-prototype-every.html

  • ForwardingHeaders/runtime/JSStringInlines.h: Added.
  • Modules/plugins/QuickTimePluginReplacement.cpp:
  • bindings/js/JSIDBAnyCustom.cpp:
  • bindings/js/JSIDBDatabaseCustom.cpp:
  • bindings/js/JSIDBObjectStoreCustom.cpp:

Source/WebKit:

CMake updates

  • CMakeLists.txt:

Source/WebKit2:

CMake updates

  • CMakeLists.txt:

Tools:

CMake updates

  • DumpRenderTree/CMakeLists.txt:
  • WebKitTestRunner/CMakeLists.txt:
  • WinCELauncher/CMakeLists.txt:

LayoutTests:

Updated the test results for new error messages (now that they're
actually helpful), and added a js-regress test to track performance.

  • js/array-every-expected.txt:
  • js/dom/array-prototype-properties-expected.txt:
  • js/regress/array-prototype-every-expected.txt: Added.
  • js/regress/array-prototype-every.html: Added.
  • js/regress/script-tests/array-prototype-every.js: Added.

(test1):
(test2):
(test3):

  • Property svn:eol-style set to native
File size: 29.3 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012, 2013 Apple Inc. All rights reserved.
4 * Copyright (C) 2006 Bjoern Graf ([email protected])
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24
25#include "APIShims.h"
26#include "ButterflyInlines.h"
27#include "BytecodeGenerator.h"
28#include "CallFrameInlines.h"
29#include "Completion.h"
30#include "CopiedSpaceInlines.h"
31#include "ExceptionHelpers.h"
32#include "HeapStatistics.h"
33#include "InitializeThreading.h"
34#include "Interpreter.h"
35#include "JSArray.h"
36#include "JSArrayBuffer.h"
37#include "JSFunction.h"
38#include "JSLock.h"
39#include "JSProxy.h"
40#include "JSString.h"
41#include "JSStringInlines.h"
42#include "Operations.h"
43#include "SamplingTool.h"
44#include "StackVisitor.h"
45#include "StructureRareDataInlines.h"
46#include "TestRunnerUtils.h"
47#include <math.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <thread>
52#include <wtf/CurrentTime.h>
53#include <wtf/MainThread.h>
54#include <wtf/StringPrintStream.h>
55#include <wtf/text/StringBuilder.h>
56
57#if !OS(WINDOWS)
58#include <unistd.h>
59#endif
60
61#if HAVE(READLINE)
62// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h
63// We #define it to something else to avoid this conflict.
64#define Function ReadlineFunction
65#include <readline/history.h>
66#include <readline/readline.h>
67#undef Function
68#endif
69
70#if HAVE(SYS_TIME_H)
71#include <sys/time.h>
72#endif
73
74#if HAVE(SIGNAL_H)
75#include <signal.h>
76#endif
77
78#if COMPILER(MSVC) && !OS(WINCE)
79#include <crtdbg.h>
80#include <mmsystem.h>
81#include <windows.h>
82#endif
83
84#if PLATFORM(IOS) && CPU(ARM_THUMB2)
85#include <fenv.h>
86#include <arm/arch.h>
87#endif
88
89#if PLATFORM(EFL)
90#include <Ecore.h>
91#endif
92
93using namespace JSC;
94using namespace WTF;
95
96static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer);
97
98static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
99static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
100static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
101static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
102static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
103#ifndef NDEBUG
104static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
105static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
106#endif
107static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
108static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
109static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
110static EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState*);
111static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
112static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
113static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
114static EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState*);
115static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
116static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
117static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
118
119#if ENABLE(SAMPLING_FLAGS)
120static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
121static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
122#endif
123
124struct Script {
125 bool isFile;
126 char* argument;
127
128 Script(bool isFile, char *argument)
129 : isFile(isFile)
130 , argument(argument)
131 {
132 }
133};
134
135class CommandLine {
136public:
137 CommandLine(int argc, char** argv)
138 : m_interactive(false)
139 , m_dump(false)
140 , m_exitCode(false)
141 , m_profile(false)
142 {
143 parseArguments(argc, argv);
144 }
145
146 bool m_interactive;
147 bool m_dump;
148 bool m_exitCode;
149 Vector<Script> m_scripts;
150 Vector<String> m_arguments;
151 bool m_profile;
152 String m_profilerOutput;
153
154 void parseArguments(int, char**);
155};
156
157static const char interactivePrompt[] = ">>> ";
158
159class StopWatch {
160public:
161 void start();
162 void stop();
163 long getElapsedMS(); // call stop() first
164
165private:
166 double m_startTime;
167 double m_stopTime;
168};
169
170void StopWatch::start()
171{
172 m_startTime = monotonicallyIncreasingTime();
173}
174
175void StopWatch::stop()
176{
177 m_stopTime = monotonicallyIncreasingTime();
178}
179
180long StopWatch::getElapsedMS()
181{
182 return static_cast<long>((m_stopTime - m_startTime) * 1000);
183}
184
185class GlobalObject : public JSGlobalObject {
186private:
187 GlobalObject(VM&, Structure*);
188
189public:
190 typedef JSGlobalObject Base;
191
192 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
193 {
194 GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure);
195 object->finishCreation(vm, arguments);
196 vm.heap.addFinalizer(object, destroy);
197 object->setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, object, object->prototype()), object));
198 return object;
199 }
200
201 static const bool needsDestruction = false;
202
203 DECLARE_INFO;
204 static const GlobalObjectMethodTable s_globalObjectMethodTable;
205
206 static Structure* createStructure(VM& vm, JSValue prototype)
207 {
208 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
209 }
210
211 static bool javaScriptExperimentsEnabled(const JSGlobalObject*) { return true; }
212
213protected:
214 void finishCreation(VM& vm, const Vector<String>& arguments)
215 {
216 Base::finishCreation(vm);
217
218 addFunction(vm, "debug", functionDebug, 1);
219 addFunction(vm, "describe", functionDescribe, 1);
220 addFunction(vm, "print", functionPrint, 1);
221 addFunction(vm, "quit", functionQuit, 0);
222 addFunction(vm, "gc", functionGC, 0);
223#ifndef NDEBUG
224 addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
225 addFunction(vm, "releaseExecutableMemory", functionReleaseExecutableMemory, 0);
226#endif
227 addFunction(vm, "version", functionVersion, 1);
228 addFunction(vm, "run", functionRun, 1);
229 addFunction(vm, "load", functionLoad, 1);
230 addFunction(vm, "readFile", functionReadFile, 1);
231 addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
232 addFunction(vm, "jscStack", functionJSCStack, 1);
233 addFunction(vm, "readline", functionReadline, 0);
234 addFunction(vm, "preciseTime", functionPreciseTime, 0);
235 addFunction(vm, "neverInlineFunction", functionNeverInlineFunction, 1);
236 addFunction(vm, "noInline", functionNeverInlineFunction, 1);
237 addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
238 addFunction(vm, "transferArrayBuffer", functionTransferArrayBuffer, 1);
239#if ENABLE(SAMPLING_FLAGS)
240 addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
241 addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
242#endif
243
244 JSArray* array = constructEmptyArray(globalExec(), 0);
245 for (size_t i = 0; i < arguments.size(); ++i)
246 array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
247 putDirect(vm, Identifier(globalExec(), "arguments"), array);
248 }
249
250 void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
251 {
252 Identifier identifier(&vm, name);
253 putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function));
254 }
255
256 void addConstructableFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
257 {
258 Identifier identifier(&vm, name);
259 putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function, NoIntrinsic, function));
260 }
261};
262
263const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
264const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled, 0, &shouldInterruptScriptBeforeTimeout };
265
266
267GlobalObject::GlobalObject(VM& vm, Structure* structure)
268 : JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
269{
270}
271
272static inline String stringFromUTF(const char* utf8)
273{
274 // Find the the first non-ascii character, or nul.
275 const char* pos = utf8;
276 while (*pos > 0)
277 pos++;
278 size_t asciiLength = pos - utf8;
279
280 // Fast case - string is all ascii.
281 if (!*pos)
282 return String(utf8, asciiLength);
283
284 // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
285 ASSERT(*pos < 0);
286 ASSERT(strlen(utf8) == asciiLength + strlen(pos));
287 return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
288}
289
290static inline SourceCode jscSource(const char* utf8, const String& filename)
291{
292 String str = stringFromUTF(utf8);
293 return makeSource(str, filename);
294}
295
296EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
297{
298 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
299 if (i)
300 putchar(' ');
301
302 printf("%s", exec->uncheckedArgument(i).toString(exec)->value(exec).utf8().data());
303 }
304
305 putchar('\n');
306 fflush(stdout);
307 return JSValue::encode(jsUndefined());
308}
309
310#ifndef NDEBUG
311EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec)
312{
313 if (!exec->callerFrame()->isVMEntrySentinel())
314 exec->vm().interpreter->dumpCallFrame(exec->callerFrame());
315 return JSValue::encode(jsUndefined());
316}
317#endif
318
319EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
320{
321 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data());
322 return JSValue::encode(jsUndefined());
323}
324
325EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
326{
327 fprintf(stderr, "--> %s\n", toCString(exec->argument(0)).data());
328 return JSValue::encode(jsUndefined());
329}
330
331class FunctionJSCStackFunctor {
332public:
333 FunctionJSCStackFunctor(StringBuilder& trace)
334 : m_trace(trace)
335 {
336 }
337
338 StackVisitor::Status operator()(StackVisitor& visitor)
339 {
340 m_trace.append(String::format(" %zu %s\n", visitor->index(), visitor->toString().utf8().data()));
341 return StackVisitor::Continue;
342 }
343
344private:
345 StringBuilder& m_trace;
346};
347
348EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
349{
350 StringBuilder trace;
351 trace.appendLiteral("--> Stack trace:\n");
352
353 FunctionJSCStackFunctor functor(trace);
354 exec->iterate(functor);
355 fprintf(stderr, "%s", trace.toString().utf8().data());
356 return JSValue::encode(jsUndefined());
357}
358
359EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
360{
361 JSLockHolder lock(exec);
362 exec->heap()->collectAllGarbage();
363 return JSValue::encode(jsUndefined());
364}
365
366#ifndef NDEBUG
367EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
368{
369 JSLockHolder lock(exec);
370 exec->vm().releaseExecutableMemory();
371 return JSValue::encode(jsUndefined());
372}
373#endif
374
375EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
376{
377 // We need this function for compatibility with the Mozilla JS tests but for now
378 // we don't actually do any version-specific handling
379 return JSValue::encode(jsUndefined());
380}
381
382EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
383{
384 String fileName = exec->argument(0).toString(exec)->value(exec);
385 Vector<char> script;
386 if (!fillBufferWithContentsOfFile(fileName, script))
387 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
388
389 GlobalObject* globalObject = GlobalObject::create(exec->vm(), GlobalObject::createStructure(exec->vm(), jsNull()), Vector<String>());
390
391 JSArray* array = constructEmptyArray(globalObject->globalExec(), 0);
392 for (unsigned i = 1; i < exec->argumentCount(); ++i)
393 array->putDirectIndex(globalObject->globalExec(), i - 1, exec->uncheckedArgument(i));
394 globalObject->putDirect(
395 exec->vm(), Identifier(globalObject->globalExec(), "arguments"), array);
396
397 JSValue exception;
398 StopWatch stopWatch;
399 stopWatch.start();
400 evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &exception);
401 stopWatch.stop();
402
403 if (!!exception) {
404 exec->vm().throwException(globalObject->globalExec(), exception);
405 return JSValue::encode(jsUndefined());
406 }
407
408 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
409}
410
411EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
412{
413 String fileName = exec->argument(0).toString(exec)->value(exec);
414 Vector<char> script;
415 if (!fillBufferWithContentsOfFile(fileName, script))
416 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
417
418 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
419
420 JSValue evaluationException;
421 JSValue result = evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &evaluationException);
422 if (evaluationException)
423 exec->vm().throwException(exec, evaluationException);
424 return JSValue::encode(result);
425}
426
427EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState* exec)
428{
429 String fileName = exec->argument(0).toString(exec)->value(exec);
430 Vector<char> script;
431 if (!fillBufferWithContentsOfFile(fileName, script))
432 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
433
434 return JSValue::encode(jsString(exec, stringFromUTF(script.data())));
435}
436
437EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
438{
439 String fileName = exec->argument(0).toString(exec)->value(exec);
440 Vector<char> script;
441 if (!fillBufferWithContentsOfFile(fileName, script))
442 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
443
444 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
445
446 StopWatch stopWatch;
447 stopWatch.start();
448
449 JSValue syntaxException;
450 bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException);
451 stopWatch.stop();
452
453 if (!validSyntax)
454 exec->vm().throwException(exec, syntaxException);
455 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
456}
457
458#if ENABLE(SAMPLING_FLAGS)
459EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
460{
461 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
462 unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
463 if ((flag >= 1) && (flag <= 32))
464 SamplingFlags::setFlag(flag);
465 }
466 return JSValue::encode(jsNull());
467}
468
469EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
470{
471 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
472 unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
473 if ((flag >= 1) && (flag <= 32))
474 SamplingFlags::clearFlag(flag);
475 }
476 return JSValue::encode(jsNull());
477}
478#endif
479
480EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
481{
482 Vector<char, 256> line;
483 int c;
484 while ((c = getchar()) != EOF) {
485 // FIXME: Should we also break on \r?
486 if (c == '\n')
487 break;
488 line.append(c);
489 }
490 line.append('\0');
491 return JSValue::encode(jsString(exec, line.data()));
492}
493
494EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*)
495{
496 return JSValue::encode(jsNumber(currentTime()));
497}
498
499EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState* exec)
500{
501 return JSValue::encode(setNeverInline(exec));
502}
503
504EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState* exec)
505{
506 return JSValue::encode(numberOfDFGCompiles(exec));
507}
508
509EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState* exec)
510{
511 if (exec->argumentCount() < 1)
512 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Not enough arguments")));
513
514 JSArrayBuffer* buffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(0));
515 if (!buffer)
516 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Expected an array buffer")));
517
518 ArrayBufferContents dummyContents;
519 buffer->impl()->transfer(dummyContents);
520
521 return JSValue::encode(jsUndefined());
522}
523
524EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*)
525{
526 exit(EXIT_SUCCESS);
527
528#if COMPILER(MSVC) && OS(WINCE)
529 // Without this, Visual Studio will complain that this method does not return a value.
530 return JSValue::encode(jsUndefined());
531#endif
532}
533
534// Use SEH for Release builds only to get rid of the crash report dialog
535// (luckily the same tests fail in Release and Debug builds so far). Need to
536// be in a separate main function because the jscmain function requires object
537// unwinding.
538
539#if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
540#define TRY __try {
541#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
542#else
543#define TRY
544#define EXCEPT(x)
545#endif
546
547int jscmain(int argc, char** argv);
548
549static double s_desiredTimeout;
550
551static NO_RETURN_DUE_TO_CRASH void timeoutThreadMain(void*)
552{
553 auto timeout = std::chrono::microseconds(static_cast<std::chrono::microseconds::rep>(s_desiredTimeout * 1000000));
554 std::this_thread::sleep_for(timeout);
555
556 dataLog("Timed out after ", s_desiredTimeout, " seconds!\n");
557 CRASH();
558}
559
560int main(int argc, char** argv)
561{
562#if PLATFORM(IOS) && CPU(ARM_THUMB2)
563 // Enabled IEEE754 denormal support.
564 fenv_t env;
565 fegetenv( &env );
566 env.__fpscr &= ~0x01000000u;
567 fesetenv( &env );
568#endif
569
570#if OS(WINDOWS)
571#if !OS(WINCE)
572 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
573 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
574 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
575 ::SetErrorMode(0);
576
577#if defined(_DEBUG)
578 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
579 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
580 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
581 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
582 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
583 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
584#endif
585#endif
586
587 timeBeginPeriod(1);
588#endif
589
590#if PLATFORM(EFL)
591 ecore_init();
592#endif
593
594 // Initialize JSC before getting VM.
595#if ENABLE(SAMPLING_REGIONS)
596 WTF::initializeMainThread();
597#endif
598 JSC::initializeThreading();
599
600#if !OS(WINCE)
601 if (char* timeoutString = getenv("JSC_timeout")) {
602 if (sscanf(timeoutString, "%lf", &s_desiredTimeout) != 1) {
603 dataLog(
604 "WARNING: timeout string is malformed, got ", timeoutString,
605 " but expected a number. Not using a timeout.\n");
606 } else
607 createThread(timeoutThreadMain, 0, "jsc Timeout Thread");
608 }
609#endif
610
611#if PLATFORM(IOS)
612 Options::crashIfCantAllocateJITMemory() = true;
613#endif
614
615 // We can't use destructors in the following code because it uses Windows
616 // Structured Exception Handling
617 int res = 0;
618 TRY
619 res = jscmain(argc, argv);
620 EXCEPT(res = 3)
621 if (Options::logHeapStatisticsAtExit())
622 HeapStatistics::reportSuccess();
623
624#if PLATFORM(EFL)
625 ecore_shutdown();
626#endif
627
628 return res;
629}
630
631static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
632{
633 const char* script;
634 String fileName;
635 Vector<char> scriptBuffer;
636
637 if (dump)
638 JSC::Options::dumpGeneratedBytecodes() = true;
639
640 VM& vm = globalObject->vm();
641
642#if ENABLE(SAMPLING_FLAGS)
643 SamplingFlags::start();
644#endif
645
646 bool success = true;
647 for (size_t i = 0; i < scripts.size(); i++) {
648 if (scripts[i].isFile) {
649 fileName = scripts[i].argument;
650 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
651 return false; // fail early so we can catch missing files
652 script = scriptBuffer.data();
653 } else {
654 script = scripts[i].argument;
655 fileName = "[Command Line]";
656 }
657
658 vm.startSampling();
659
660 JSValue evaluationException;
661 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), &evaluationException);
662 success = success && !evaluationException;
663 if (dump && !evaluationException)
664 printf("End: %s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
665 if (evaluationException) {
666 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
667 Identifier stackID(globalObject->globalExec(), "stack");
668 JSValue stackValue = evaluationException.get(globalObject->globalExec(), stackID);
669 if (!stackValue.isUndefinedOrNull())
670 printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
671 }
672
673 vm.stopSampling();
674 globalObject->globalExec()->clearException();
675 }
676
677#if ENABLE(SAMPLING_FLAGS)
678 SamplingFlags::stop();
679#endif
680#if ENABLE(SAMPLING_REGIONS)
681 SamplingRegion::dump();
682#endif
683 vm.dumpSampleData(globalObject->globalExec());
684#if ENABLE(SAMPLING_COUNTERS)
685 AbstractSamplingCounter::dump();
686#endif
687#if ENABLE(REGEXP_TRACING)
688 vm.dumpRegExpTrace();
689#endif
690 return success;
691}
692
693#define RUNNING_FROM_XCODE 0
694
695static void runInteractive(GlobalObject* globalObject)
696{
697 String interpreterName("Interpreter");
698
699 bool shouldQuit = false;
700 while (!shouldQuit) {
701#if HAVE(READLINE) && !RUNNING_FROM_XCODE
702 ParserError error;
703 String source;
704 do {
705 error = ParserError();
706 char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
707 shouldQuit = !line;
708 if (!line)
709 break;
710 source = source + line;
711 source = source + '\n';
712 checkSyntax(globalObject->vm(), makeSource(source, interpreterName), error);
713 if (!line[0])
714 break;
715 add_history(line);
716 } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable);
717
718 if (error.m_type != ParserError::ErrorNone) {
719 printf("%s:%d\n", error.m_message.utf8().data(), error.m_line);
720 continue;
721 }
722
723
724 JSValue evaluationException;
725 JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException);
726#else
727 printf("%s", interactivePrompt);
728 Vector<char, 256> line;
729 int c;
730 while ((c = getchar()) != EOF) {
731 // FIXME: Should we also break on \r?
732 if (c == '\n')
733 break;
734 line.append(c);
735 }
736 if (line.isEmpty())
737 break;
738 line.append('\0');
739
740 JSValue evaluationException;
741 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line.data(), interpreterName), JSValue(), &evaluationException);
742#endif
743 if (evaluationException)
744 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
745 else
746 printf("%s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
747
748 globalObject->globalExec()->clearException();
749 }
750 printf("\n");
751}
752
753static NO_RETURN void printUsageStatement(bool help = false)
754{
755 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
756 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
757 fprintf(stderr, " -e Evaluate argument as script code\n");
758 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
759 fprintf(stderr, " -h|--help Prints this help message\n");
760 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
761#if HAVE(SIGNAL_H)
762 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
763#endif
764 fprintf(stderr, " -p <file> Outputs profiling data to a file\n");
765 fprintf(stderr, " -x Output exit code before terminating\n");
766 fprintf(stderr, "\n");
767 fprintf(stderr, " --options Dumps all JSC VM options and exits\n");
768 fprintf(stderr, " --dumpOptions Dumps all JSC VM options before continuing\n");
769 fprintf(stderr, " --<jsc VM option>=<value> Sets the specified JSC VM option\n");
770 fprintf(stderr, "\n");
771
772 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
773}
774
775void CommandLine::parseArguments(int argc, char** argv)
776{
777 int i = 1;
778 bool needToDumpOptions = false;
779 bool needToExit = false;
780
781 for (; i < argc; ++i) {
782 const char* arg = argv[i];
783 if (!strcmp(arg, "-f")) {
784 if (++i == argc)
785 printUsageStatement();
786 m_scripts.append(Script(true, argv[i]));
787 continue;
788 }
789 if (!strcmp(arg, "-e")) {
790 if (++i == argc)
791 printUsageStatement();
792 m_scripts.append(Script(false, argv[i]));
793 continue;
794 }
795 if (!strcmp(arg, "-i")) {
796 m_interactive = true;
797 continue;
798 }
799 if (!strcmp(arg, "-d")) {
800 m_dump = true;
801 continue;
802 }
803 if (!strcmp(arg, "-p")) {
804 if (++i == argc)
805 printUsageStatement();
806 m_profile = true;
807 m_profilerOutput = argv[i];
808 continue;
809 }
810 if (!strcmp(arg, "-s")) {
811#if HAVE(SIGNAL_H)
812 signal(SIGILL, _exit);
813 signal(SIGFPE, _exit);
814 signal(SIGBUS, _exit);
815 signal(SIGSEGV, _exit);
816#endif
817 continue;
818 }
819 if (!strcmp(arg, "-x")) {
820 m_exitCode = true;
821 continue;
822 }
823 if (!strcmp(arg, "--")) {
824 ++i;
825 break;
826 }
827 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
828 printUsageStatement(true);
829
830 if (!strcmp(arg, "--options")) {
831 needToDumpOptions = true;
832 needToExit = true;
833 continue;
834 }
835 if (!strcmp(arg, "--dumpOptions")) {
836 needToDumpOptions = true;
837 continue;
838 }
839
840 // See if the -- option is a JSC VM option.
841 // NOTE: At this point, we know that the arg starts with "--". Skip it.
842 if (JSC::Options::setOption(&arg[2])) {
843 // The arg was recognized as a VM option and has been parsed.
844 continue; // Just continue with the next arg.
845 }
846
847 // This arg is not recognized by the VM nor by jsc. Pass it on to the
848 // script.
849 m_scripts.append(Script(true, argv[i]));
850 }
851
852 if (m_scripts.isEmpty())
853 m_interactive = true;
854
855 for (; i < argc; ++i)
856 m_arguments.append(argv[i]);
857
858 if (needToDumpOptions)
859 JSC::Options::dumpAllOptions(stderr);
860 if (needToExit)
861 exit(EXIT_SUCCESS);
862}
863
864int jscmain(int argc, char** argv)
865{
866 // Note that the options parsing can affect VM creation, and thus
867 // comes first.
868 CommandLine options(argc, argv);
869 VM* vm = VM::create(LargeHeap).leakRef();
870 int result;
871 {
872 APIEntryShim shim(vm);
873
874 if (options.m_profile && !vm->m_perBytecodeProfiler)
875 vm->m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*vm));
876
877 GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.m_arguments);
878 bool success = runWithScripts(globalObject, options.m_scripts, options.m_dump);
879 if (options.m_interactive && success)
880 runInteractive(globalObject);
881
882 result = success ? 0 : 3;
883
884 if (options.m_exitCode)
885 printf("jsc exiting %d\n", result);
886
887 if (options.m_profile) {
888 if (!vm->m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data()))
889 fprintf(stderr, "could not save profiler output.\n");
890 }
891 }
892
893 return result;
894}
895
896static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer)
897{
898 FILE* f = fopen(fileName.utf8().data(), "r");
899 if (!f) {
900 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
901 return false;
902 }
903
904 size_t bufferSize = 0;
905 size_t bufferCapacity = 1024;
906
907 buffer.resize(bufferCapacity);
908
909 while (!feof(f) && !ferror(f)) {
910 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
911 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
912 bufferCapacity *= 2;
913 buffer.resize(bufferCapacity);
914 }
915 }
916 fclose(f);
917 buffer[bufferSize] = '\0';
918
919 if (buffer[0] == '#' && buffer[1] == '!')
920 buffer[0] = buffer[1] = '/';
921
922 return true;
923}
Note: See TracBrowser for help on using the repository browser.