1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003-2019 Apple Inc.
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#include "Completion.h"
25
26#include "CallFrame.h"
27#include "CatchScope.h"
28#include "CodeCache.h"
29#include "CodeProfiling.h"
30#include "Exception.h"
31#include "IdentifierInlines.h"
32#include "Interpreter.h"
33#include "JSCInlines.h"
34#include "JSGlobalObject.h"
35#include "JSInternalPromise.h"
36#include "JSInternalPromiseDeferred.h"
37#include "JSLock.h"
38#include "JSModuleLoader.h"
39#include "JSModuleRecord.h"
40#include "JSWithScope.h"
41#include "ModuleAnalyzer.h"
42#include "Parser.h"
43#include "ProgramExecutable.h"
44#include "ScriptProfilingScope.h"
45
46namespace JSC {
47
48static inline bool checkSyntaxInternal(VM& vm, const SourceCode& source, ParserError& error)
49{
50 return !!parse<ProgramNode>(
51 &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin,
52 JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error);
53}
54
55bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedException)
56{
57 VM& vm = exec->vm();
58 JSLockHolder lock(vm);
59 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
60
61 ParserError error;
62 if (checkSyntaxInternal(vm, source, error))
63 return true;
64 ASSERT(error.isValid());
65 if (returnedException)
66 *returnedException = error.toErrorObject(exec->lexicalGlobalObject(), source);
67 return false;
68}
69
70bool checkSyntax(VM& vm, const SourceCode& source, ParserError& error)
71{
72 JSLockHolder lock(vm);
73 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
74 return checkSyntaxInternal(vm, source, error);
75}
76
77bool checkModuleSyntax(ExecState* exec, const SourceCode& source, ParserError& error)
78{
79 VM& vm = exec->vm();
80 JSLockHolder lock(vm);
81 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
82 std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>(
83 &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin,
84 JSParserStrictMode::Strict, JSParserScriptMode::Module, SourceParseMode::ModuleAnalyzeMode, SuperBinding::NotNeeded, error);
85 if (!moduleProgramNode)
86 return false;
87
88 PrivateName privateName(PrivateName::Description, "EntryPointModule");
89 ModuleAnalyzer moduleAnalyzer(exec, Identifier::fromUid(privateName), source, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables());
90 moduleAnalyzer.analyze(*moduleProgramNode);
91 return true;
92}
93
94CachedBytecode generateProgramBytecode(VM& vm, const SourceCode& source, ParserError& error)
95{
96 JSLockHolder lock(vm);
97 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
98
99 VariableEnvironment variablesUnderTDZ;
100 JSParserStrictMode strictMode = JSParserStrictMode::NotStrict;
101 JSParserScriptMode scriptMode = JSParserScriptMode::Classic;
102 DebuggerMode debuggerMode = DebuggerOff;
103 EvalContextType evalContextType = EvalContextType::None;
104
105 UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
106 if (!unlinkedCodeBlock)
107 return { };
108 return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, debuggerMode);
109}
110
111CachedBytecode generateModuleBytecode(VM& vm, const SourceCode& source, ParserError& error)
112{
113 JSLockHolder lock(vm);
114 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
115
116 VariableEnvironment variablesUnderTDZ;
117 JSParserStrictMode strictMode = JSParserStrictMode::Strict;
118 JSParserScriptMode scriptMode = JSParserScriptMode::Module;
119 DebuggerMode debuggerMode = DebuggerOff;
120 EvalContextType evalContextType = EvalContextType::None;
121
122 UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
123 if (!unlinkedCodeBlock)
124 return { };
125 return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, debuggerMode);
126}
127
128JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException)
129{
130 VM& vm = exec->vm();
131 JSLockHolder lock(vm);
132 auto scope = DECLARE_CATCH_SCOPE(vm);
133 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
134 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
135
136 CodeProfiling profile(source);
137
138 if (!thisValue || thisValue.isUndefinedOrNull())
139 thisValue = vm.vmEntryGlobalObject(exec);
140 JSObject* thisObj = jsCast<JSObject*>(thisValue.toThis(exec, NotStrictMode));
141 JSValue result = vm.interpreter->executeProgram(source, exec, thisObj);
142
143 if (scope.exception()) {
144 returnedException = scope.exception();
145 scope.clearException();
146 return jsUndefined();
147 }
148
149 RELEASE_ASSERT(result);
150 return result;
151}
152
153JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException)
154{
155 VM& vm = exec->vm();
156 ScriptProfilingScope profilingScope(vm.vmEntryGlobalObject(exec), reason);
157 return evaluate(exec, source, thisValue, returnedException);
158}
159
160JSValue evaluateWithScopeExtension(ExecState* exec, const SourceCode& source, JSObject* scopeExtensionObject, NakedPtr<Exception>& returnedException)
161{
162 VM& vm = exec->vm();
163 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
164
165 if (scopeExtensionObject) {
166 JSScope* ignoredPreviousScope = globalObject->globalScope();
167 globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject));
168 }
169
170 JSValue returnValue = JSC::evaluate(globalObject->globalExec(), source, globalObject, returnedException);
171
172 if (scopeExtensionObject)
173 globalObject->clearGlobalScopeExtension();
174
175 return returnValue;
176}
177
178static Symbol* createSymbolForEntryPointModule(VM& vm)
179{
180 // Generate the unique key for the source-provided module.
181 PrivateName privateName(PrivateName::Description, "EntryPointModule");
182 return Symbol::create(vm, privateName.uid());
183}
184
185static JSInternalPromise* rejectPromise(ExecState* exec, JSGlobalObject* globalObject)
186{
187 VM& vm = exec->vm();
188 auto scope = DECLARE_CATCH_SCOPE(vm);
189 scope.assertNoException();
190 JSValue exception = scope.exception()->value();
191 scope.clearException();
192 JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
193 scope.releaseAssertNoException();
194 deferred->reject(exec, exception);
195 scope.releaseAssertNoException();
196 return deferred->promise();
197}
198
199JSInternalPromise* loadAndEvaluateModule(ExecState* exec, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher)
200{
201 VM& vm = exec->vm();
202 JSLockHolder lock(vm);
203 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
204 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
205
206 return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, moduleId, parameters, scriptFetcher);
207}
208
209JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher)
210{
211 VM& vm = exec->vm();
212 JSLockHolder lock(vm);
213 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
214 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
215
216 return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher);
217}
218
219JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher)
220{
221 VM& vm = exec->vm();
222 JSLockHolder lock(vm);
223 auto scope = DECLARE_THROW_SCOPE(vm);
224 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
225 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
226
227 Symbol* key = createSymbolForEntryPointModule(vm);
228
229 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
230
231 // Insert the given source code to the ModuleLoader registry as the fetched registry entry.
232 globalObject->moduleLoader()->provideFetch(exec, key, source);
233 RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject));
234
235 return globalObject->moduleLoader()->loadAndEvaluateModule(exec, key, jsUndefined(), scriptFetcher);
236}
237
238JSInternalPromise* loadModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher)
239{
240 VM& vm = exec->vm();
241 JSLockHolder lock(vm);
242 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
243 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
244
245 return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher);
246}
247
248JSInternalPromise* loadModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher)
249{
250 VM& vm = exec->vm();
251 JSLockHolder lock(vm);
252 auto scope = DECLARE_THROW_SCOPE(vm);
253 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
254 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
255
256 Symbol* key = createSymbolForEntryPointModule(vm);
257
258 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
259
260 // Insert the given source code to the ModuleLoader registry as the fetched registry entry.
261 // FIXME: Introduce JSSourceCode object to wrap around this source.
262 globalObject->moduleLoader()->provideFetch(exec, key, source);
263 RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject));
264
265 return globalObject->moduleLoader()->loadModule(exec, key, jsUndefined(), scriptFetcher);
266}
267
268JSValue linkAndEvaluateModule(ExecState* exec, const Identifier& moduleKey, JSValue scriptFetcher)
269{
270 VM& vm = exec->vm();
271 JSLockHolder lock(vm);
272 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
273 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
274
275 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
276 return globalObject->moduleLoader()->linkAndEvaluateModule(exec, identifierToJSValue(vm, moduleKey), scriptFetcher);
277}
278
279JSInternalPromise* importModule(ExecState* exec, const Identifier& moduleKey, JSValue parameters, JSValue scriptFetcher)
280{
281 VM& vm = exec->vm();
282 JSLockHolder lock(vm);
283 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
284 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
285
286 return vm.vmEntryGlobalObject(exec)->moduleLoader()->requestImportModule(exec, moduleKey, parameters, scriptFetcher);
287}
288
289} // namespace JSC
290