File indexing completed on 2024-05-12 15:43:25

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2006 Harri Porten (porten@kde.org)
0004  *
0005  *  This library is free software; you can redistribute it and/or
0006  *  modify it under the terms of the GNU Library General Public
0007  *  License as published by the Free Software Foundation; either
0008  *  version 2 of the License, or (at your option) any later version.
0009  *
0010  *  This library is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  *  Library General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU Library General Public License
0016  *  along with this library; see the file COPYING.LIB.  If not, write to
0017  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  *  Boston, MA 02110-1301, USA.
0019  *
0020  */
0021 
0022 #include "JSLock.h"
0023 #include "interpreter.h"
0024 #include "object.h"
0025 #include "package.h"
0026 #include "function.h"
0027 
0028 #include <cstring>
0029 #include <stdlib.h>
0030 #include <stdio.h>
0031 #include <sys/stat.h>
0032 #include <fcntl.h>
0033 
0034 #if PLATFORM(WIN_OS)
0035 #  include <windows.h>
0036 #  include <io.h>
0037 #else
0038 #  include <unistd.h>
0039 #endif
0040 
0041 #include <kjs_version.h>
0042 
0043 enum ExitCode { ErrorNone,
0044                 ErrorUnknownSwitch,
0045                 ErrorMissingArg,
0046                 ErrorReadFile,
0047                 ErrorEval
0048               };
0049 
0050 using std::strcmp;
0051 
0052 using namespace KJS;
0053 
0054 static void printUsage(const char *app)
0055 {
0056     fprintf(stderr,
0057             "Usage: %s\n"
0058             "  [ -h | -help | --help ]\n"
0059             "  [ -e <statement> | <script> ]\n"
0060             "  [-v | -version | --version]\n",
0061             app);
0062 }
0063 
0064 static UString readFile(const char *fileName)
0065 {
0066     int fd = open(fileName, O_RDONLY);
0067     if (fd < 0) {
0068         fprintf(stderr, "Error opening %s", fileName);
0069         return UString();
0070     }
0071     struct stat buf;
0072     if (fstat(fd, &buf) == -1) {
0073         fprintf(stderr, "Error stat'ing %s", fileName);
0074         close(fd);
0075         return UString();
0076     }
0077     int siz = buf.st_size;
0078     char *c = new char[siz + 1];
0079     int dataRead = read(fd, c, siz);
0080     if (dataRead == -1) {
0081         fprintf(stderr, "Error reading from %s", fileName);
0082         delete[] c;
0083         close(fd);
0084         return UString();
0085     }
0086     c[dataRead] = '\0';
0087     UString s = c;
0088     delete[] c;
0089     close(fd);
0090     return s;
0091 }
0092 
0093 static ExitCode evaluateFile(Interpreter *interp, const char *fileName);
0094 
0095 class GlobalImp : public JSGlobalObject
0096 {
0097 public:
0098     UString className() const override
0099     {
0100         return "global";
0101     }
0102 };
0103 
0104 class TestFunctionImp : public JSObject
0105 {
0106 public:
0107     TestFunctionImp(int i, int length);
0108     bool implementsCall() const override
0109     {
0110         return true;
0111     }
0112     JSValue *callAsFunction(ExecState *exec,
0113                                     JSObject *thisObj, const List &args) override;
0114     enum { Print, Quit, Load, GC };
0115 
0116 private:
0117     int id;
0118 };
0119 
0120 TestFunctionImp::TestFunctionImp(int i, int length)
0121     : JSObject(), id(i)
0122 {
0123     putDirect(Identifier("length"), length, DontDelete | ReadOnly | DontEnum);
0124 }
0125 
0126 JSValue *TestFunctionImp::callAsFunction(ExecState *exec,
0127         JSObject * /* thisObj */,
0128         const List &args)
0129 {
0130     switch (id) {
0131     case Print:
0132         printf("%s\n", JSValue::toString(args[0], exec).UTF8String().c_str());
0133         return jsUndefined();
0134     case Quit:
0135         exit(0);
0136     case GC:
0137         while (Interpreter::collect()) {}
0138         break;
0139     case Load:
0140         evaluateFile(exec->dynamicInterpreter(), JSValue::toString(args[0], exec).UTF8String().c_str());
0141         break;
0142     default:
0143         abort();
0144     }
0145     return jsUndefined();
0146 }
0147 
0148 static ExitCode evaluateString(Interpreter *interp, const char *fileName,
0149                                const UString &code,
0150                                bool printResult = false)
0151 {
0152     ExecState *exec = interp->globalExec();
0153 
0154     Completion res = interp->evaluate(fileName, 0, code);
0155 
0156     if (res.complType() == Throw) {
0157         CString msg = JSValue::toString(res.value(), exec).UTF8String();
0158         JSObject *resObj = JSValue::toObject(res.value(), exec);
0159         CString message = resObj->toString(exec).UTF8String();
0160         int line = JSValue::toUInt32(resObj->toObject(exec)->get(exec, "line"), exec);
0161 
0162         if (fileName) {
0163             fprintf(stderr, "%s (line %d): ", fileName, line);
0164         }
0165         fprintf(stderr, "%s\n", msg.c_str());
0166         return ErrorEval;
0167     } else if (printResult) {
0168         if (res.isValueCompletion() && !JSValue::isUndefined(res.value())) {
0169             CString s8 = JSValue::toString(res.value(), exec).UTF8String();
0170             if (s8.size() != 0) {
0171                 fprintf(stdout, "%s\n", s8.c_str());
0172             }
0173         }
0174     }
0175 
0176     return ErrorNone;
0177 }
0178 
0179 static ExitCode evaluateFile(Interpreter *interp, const char *fileName)
0180 {
0181     UString code = readFile(fileName);
0182     if (code.isNull()) {
0183         return ErrorReadFile;
0184     }
0185 
0186     return evaluateString(interp, fileName, code);
0187 }
0188 
0189 // primitive readline-like function
0190 static char *readLine(const char *prompt)
0191 {
0192     if (prompt) {
0193         fprintf(stdout, "%s", prompt);
0194     }
0195 
0196     const int bsize = 2 << 10;
0197     char *buffer = static_cast<char *>(malloc(bsize));
0198     char *s = fgets(buffer, bsize, stdin);
0199     if (s == nullptr) {
0200         // EOF
0201         fprintf(stdout, "\n");
0202         free(buffer);
0203     }
0204     return s;
0205 }
0206 
0207 static ExitCode evaluateInteractive(Interpreter *interp)
0208 {
0209     char *line;
0210     while ((line = readLine("JS> ")) != nullptr) {
0211         UString code(line);
0212         free(line);
0213         evaluateString(interp, nullptr, code, true);
0214     }
0215 
0216     return ErrorNone;
0217 }
0218 
0219 static ExitCode parseArgs(int argc, char **argv)
0220 {
0221     JSLock lock;
0222 
0223     GlobalImp *global = new GlobalImp();
0224 
0225     // create interpreter
0226     RefPtr<Interpreter> interp = new Interpreter(global);
0227 
0228     // add some global extension functions
0229     ExecState *gexec = interp->globalExec();
0230     global->put(gexec, "print",
0231                 new TestFunctionImp(TestFunctionImp::Print, 1));
0232     global->put(gexec, "quit",
0233                 new TestFunctionImp(TestFunctionImp::Quit, 0));
0234     global->put(gexec, "load",
0235                 new TestFunctionImp(TestFunctionImp::Load, 1));
0236     global->put(gexec, "gc",
0237                 new TestFunctionImp(TestFunctionImp::GC, 0));
0238 
0239     // enable package support
0240     StandardGlobalPackage package;
0241     interp->setGlobalPackage(&package);
0242 
0243     const char *script = nullptr, *command = nullptr;
0244     int ai = 1;
0245     bool ranOtherScript = false;
0246     for (ai = 1; ai < argc; ++ai) {
0247         const char *a = argv[ai];
0248         if (strcmp(a, "-v") == 0 || strcmp(a, "-version") == 0 ||
0249                 strcmp(a, "--version") == 0) {
0250             printf("KDE: %s\n", KJS_VERSION_STRING);
0251             return ErrorNone;
0252         } else if (strcmp(a, "-h") == 0 || strcmp(a, "-help") == 0 ||
0253                    strcmp(a, "--help") == 0) {
0254             printUsage(argv[0]);
0255             return ErrorNone;
0256         } else if (strcmp(a, "-e") == 0) {
0257             ++ai;
0258             if (argc <= ai) {
0259                 fprintf(stderr, "Missing -e argument.\n");
0260                 return ErrorMissingArg;
0261             }
0262             command = argv[ai];
0263             ++ai;
0264             break;
0265         } else if (strcmp(a, "-f") == 0) { // Compatibility mode, for SunSpider
0266             ++ai;
0267             if (argc <= ai) {
0268                 fprintf(stderr, "Missing -f argument.\n");
0269                 return ErrorMissingArg;
0270             }
0271             ExitCode result = evaluateFile(interp.get(), argv[ai]);
0272             if (result != ErrorNone) {
0273                 return result;
0274             }
0275             ranOtherScript = true;
0276         } else if (a[0] == '-') {
0277             fprintf(stderr, "Unknown switch %s.\n", a);
0278             return ErrorUnknownSwitch;
0279         } else {
0280             script = a;
0281             ++ai;
0282             break;
0283         }
0284     }
0285 
0286     // ###
0287     if (argc > ai) {
0288         fprintf(stderr, "Warning: ignoring extra arguments\n");
0289     }
0290 
0291     if (script) {
0292         return evaluateFile(interp.get(), script);
0293     } else if (command) {
0294         return evaluateString(interp.get(), "(eval)", command);
0295     } else if (!ranOtherScript) {
0296         return evaluateInteractive(interp.get());
0297     }
0298     return ErrorNone;
0299 }
0300 
0301 int main(int argc, char **argv)
0302 {
0303     return int(parseArgs(argc, argv));
0304 }