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