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