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
94Ref<CachedBytecode> 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 EvalContextType evalContextType = EvalContextType::None;
103
104 UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, error, evalContextType, &variablesUnderTDZ);
105 if (!unlinkedCodeBlock)
106 return CachedBytecode::create();
107 return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, { });
108}
109
110Ref<CachedBytecode> generateModuleBytecode(VM& vm, const SourceCode& source, ParserError& error)
111{
112 JSLockHolder lock(vm);
113 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
114
115 VariableEnvironment variablesUnderTDZ;
116 JSParserStrictMode strictMode = JSParserStrictMode::Strict;
117 JSParserScriptMode scriptMode = JSParserScriptMode::Module;
118 EvalContextType evalContextType = EvalContextType::None;
119
120 UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, error, evalContextType, &variablesUnderTDZ);
121 if (!unlinkedCodeBlock)
122 return CachedBytecode::create();
123 return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, { });
124}
125
126JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException)
127{
128 VM& vm = exec->vm();
129 JSLockHolder lock(vm);
130 auto scope = DECLARE_CATCH_SCOPE(vm);
131 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
132 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
133
134 CodeProfiling profile(source);
135
136 if (!thisValue || thisValue.isUndefinedOrNull())
137 thisValue = vm.vmEntryGlobalObject(exec);
138 JSObject* thisObj = jsCast<JSObject*>(thisValue.toThis(exec, NotStrictMode));
139 JSValue result = vm.interpreter->executeProgram(source, exec, thisObj);
140
141 if (scope.exception()) {
142 returnedException = scope.exception();
143 scope.clearException();
144 return jsUndefined();
145 }
146
147 RELEASE_ASSERT(result);
148 return result;
149}
150
151JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException)
152{
153 VM& vm = exec->vm();
154 ScriptProfilingScope profilingScope(vm.vmEntryGlobalObject(exec), reason);
155 return evaluate(exec, source, thisValue, returnedException);
156}
157
158JSValue evaluateWithScopeExtension(ExecState* exec, const SourceCode& source, JSObject* scopeExtensionObject, NakedPtr<Exception>& returnedException)
159{
160 VM& vm = exec->vm();
161 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
162
163 if (scopeExtensionObject) {
164 JSScope* ignoredPreviousScope = globalObject->globalScope();
165 globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject));
166 }
167
168 JSValue returnValue = JSC::evaluate(globalObject->globalExec(), source, globalObject, returnedException);
169
170 if (scopeExtensionObject)
171 globalObject->clearGlobalScopeExtension();
172
173 return returnValue;
174}
175
176static Symbol* createSymbolForEntryPointModule(VM& vm)
177{
178 // Generate the unique key for the source-provided module.
179 PrivateName privateName(PrivateName::Description, "EntryPointModule");
180 return Symbol::create(vm, privateName.uid());
181}
182
183static JSInternalPromise* rejectPromise(ExecState* exec, JSGlobalObject* globalObject)
184{
185 VM& vm = exec->vm();
186 auto scope = DECLARE_CATCH_SCOPE(vm);
187 scope.assertNoException();
188 JSValue exception = scope.exception()->value();
189 scope.clearException();
190 JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
191 scope.releaseAssertNoException();
192 deferred->reject(exec, exception);
193 scope.releaseAssertNoException();
194 return deferred->promise();
195}
196
197JSInternalPromise* loadAndEvaluateModule(ExecState* exec, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher)
198{
199 VM& vm = exec->vm();
200 JSLockHolder lock(vm);
201 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
202 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
203
204 return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, moduleId, parameters, scriptFetcher);
205}
206
207JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher)
208{
209 VM& vm = exec->vm();
210 JSLockHolder lock(vm);
211 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
212 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
213
214 return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher);
215}
216
217JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher)
218{
219 VM& vm = exec->vm();
220 JSLockHolder lock(vm);
221 auto scope = DECLARE_THROW_SCOPE(vm);
222 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
223 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
224
225 Symbol* key = createSymbolForEntryPointModule(vm);
226
227 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
228
229 // Insert the given source code to the ModuleLoader registry as the fetched registry entry.
230 globalObject->moduleLoader()->provideFetch(exec, key, source);
231 RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject));
232
233 return globalObject->moduleLoader()->loadAndEvaluateModule(exec, key, jsUndefined(), scriptFetcher);
234}
235
236JSInternalPromise* loadModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher)
237{
238 VM& vm = exec->vm();
239 JSLockHolder lock(vm);
240 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
241 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
242
243 return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher);
244}
245
246JSInternalPromise* loadModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher)
247{
248 VM& vm = exec->vm();
249 JSLockHolder lock(vm);
250 auto scope = DECLARE_THROW_SCOPE(vm);
251 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
252 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
253
254 Symbol* key = createSymbolForEntryPointModule(vm);
255
256 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
257
258 // Insert the given source code to the ModuleLoader registry as the fetched registry entry.
259 // FIXME: Introduce JSSourceCode object to wrap around this source.
260 globalObject->moduleLoader()->provideFetch(exec, key, source);
261 RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject));
262
263 return globalObject->moduleLoader()->loadModule(exec, key, jsUndefined(), scriptFetcher);
264}
265
266JSValue linkAndEvaluateModule(ExecState* exec, const Identifier& moduleKey, JSValue scriptFetcher)
267{
268 VM& vm = exec->vm();
269 JSLockHolder lock(vm);
270 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
271 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
272
273 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
274 return globalObject->moduleLoader()->linkAndEvaluateModule(exec, identifierToJSValue(vm, moduleKey), scriptFetcher);
275}
276
277JSInternalPromise* importModule(ExecState* exec, const Identifier& moduleKey, JSValue parameters, JSValue scriptFetcher)
278{
279 VM& vm = exec->vm();
280 JSLockHolder lock(vm);
281 RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
282 RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
283
284 return vm.vmEntryGlobalObject(exec)->moduleLoader()->requestImportModule(exec, moduleKey, parameters, scriptFetcher);
285}
286
287} // namespace JSC
288