#!/usr/bin/python
# Copyright (C) 2015 Canon Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


import argparse
import filecmp
import os
import subprocess

from string import Template


parser = argparse.ArgumentParser()
parser.add_argument('--output_dir', default='', help='output directory')
parser.add_argument('--input', help='path to input JS file')
parser.add_argument('--webcore_dir', help='path to WebCore directory')

args = parser.parse_args()
filename = os.path.splitext(os.path.basename(args.input))[0]

# generate JS builtins
namespace = "WebCore"
prefix = "WEBCORE" + filename.upper()
output = os.path.join(args.output_dir, filename + "Builtins")
jsc_generate_builtin_script = os.path.join(args.webcore_dir, "..", "JavaScriptCore", "generate-js-builtins")

subprocess.call(["python", jsc_generate_builtin_script, "--output", output, "--namespace", namespace, "--prefix", prefix, args.input], stderr=subprocess.STDOUT)

# generate JS builtins wrapper
output_base = output + "Wrapper"

builtinsWrapperHeader = open(output_base + ".h.tmp", "w")

builtinsWrapperHeader.write(Template(
"""
/* Generated by generate-js-builtins do not hand edit. */

/*
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#pragma once

#include "${Filename}Builtins.h"
#include <runtime/Executable.h>
#include <builtins/BuiltinUtils.h>
#include <runtime/Identifier.h>
#include <runtime/JSFunction.h>

namespace WebCore {
    
class ${Filename}BuiltinsNames {
public:
    explicit ${Filename}BuiltinsNames(JSC::JSGlobalData& vm)
        : m_vm(vm)
        ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_BUILTIN_NAMES)
    {
    }

    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_IDENTIFIER_ACCESSOR)
    
    void exportNames();
    
private:
    JSC::JSGlobalData& m_vm;

    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_NAMES)
};

inline void ${Filename}BuiltinsNames::exportNames()
{
#define EXPORT_FUNCTION_NAME(name) m_vm.propertyNames->appendExternalName(name##PublicName(), name##PrivateName());
    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(EXPORT_FUNCTION_NAME)
#undef EXPORT_FUNCTION_NAME
}

#define INITIALIZE_BUILTIN_PUBLIC_NAME(name) , m_##name(JSC::Identifier::fromString(vm, #name))
#define DECLARE_BUILTIN_PUBLIC_NAME(name) const JSC::Identifier m_##name;
#define DECLARE_BUILTIN_PUBLIC_IDENTIFIER_ACCESSOR(name) \
    const JSC::Identifier& name##PublicName() const { return m_##name; }
    
class ${Filename}BuiltinsWrapper : private JSC::WeakHandleOwner {
public:
    explicit ${Filename}BuiltinsWrapper(JSC::JSGlobalData& vm)
        : m_vm(vm)
        ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_BUILTIN_PUBLIC_NAME)
#define INITIALIZE_BUILTIN_SOURCE_MEMBERS(name, function_name, overriddenName, length) , m_##name##Source(JSC::makeSource(StringImpl::createFromLiteral(s_##name, length), JSC::SourceOrigin()))
        ${Prefix}_FOREACH_BUILTIN(INITIALIZE_BUILTIN_SOURCE_MEMBERS)
#undef INITIALIZE_BUILTIN_SOURCE_MEMBERS
    {
    }

#define EXPOSE_BUILTIN_EXECUTABLES(name, function_name, overriddenName, length) \\
    JSC::FunctionExecutable* name##Executable(); \\
    const JSC::SourceCode& name##Source() const { return m_##name##Source; }
    ${Prefix}_FOREACH_BUILTIN(EXPOSE_BUILTIN_EXECUTABLES)
#undef EXPOSE_BUILTIN_EXECUTABLES

    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_PUBLIC_IDENTIFIER_ACCESSOR)
    
private:
    JSC::JSGlobalData& m_vm;

    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_PUBLIC_NAME)

#define DECLARE_BUILTIN_SOURCE_MEMBERS(name, function_name, overriddenName, length) \\
    JSC::SourceCode m_##name##Source;\\
    JSC::Weak<JSC::FunctionExecutable> m_##name##Executable;
    ${Prefix}_FOREACH_BUILTIN(DECLARE_BUILTIN_SOURCE_MEMBERS)
#undef DECLARE_BUILTIN_SOURCE_MEMBERS

};

#define DEFINE_BUILTIN_EXECUTABLES(name, function_name, overriddenName, length) \\
inline JSC::FunctionExecutable* ${Filename}BuiltinsWrapper::name##Executable() \\
{\\
    if (!m_##name##Executable)\\
        m_##name##Executable = JSC::PassWeak<JSC::FunctionExecutable>(JSC::createBuiltinExecutable(m_vm, m_##name##Source, function_name##PublicName(), s_##name##Intrinsic, s_##name##ConstructAbility), this, &m_##name##Executable);\\
    return m_##name##Executable.get();\\
}
${Prefix}_FOREACH_BUILTIN(DEFINE_BUILTIN_EXECUTABLES)
#undef DEFINE_BUILTIN_EXECUTABLES

class ${Filename}BuiltinFunctions {
public:
    explicit ${Filename}BuiltinFunctions(JSC::VM& vm) : m_vm(vm) { }

    void init(JSC::JSGlobalObject&);
    void visit(JSC::SlotVisitor&);

public:
    JSC::VM& m_vm;

#define DECLARE_BUILTIN_SOURCE_MEMBERS(function_name) \\
    JSC::WriteBarrier<JSC::JSFunction> m_##function_name##Function;
    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_SOURCE_MEMBERS)
#undef DECLARE_BUILTIN_SOURCE_MEMBERS
};

inline void ${Filename}BuiltinFunctions::init(JSC::JSGlobalObject& globalObject)
{
#define EXPORT_FUNCTION(codeName, function_name, overriddenName, length)\\
    m_##function_name##Function.set(m_vm, &globalObject, JSC::JSFunction::create(m_vm, codeName##Generator(&globalObject), &globalObject));
    ${Prefix}_FOREACH_BUILTIN(EXPORT_FUNCTION)
#undef EXPORT_FUNCTION
}

inline void ${Filename}BuiltinFunctions::visit(JSC::SlotVisitor& visitor)
{
#define VISIT_FUNCTION(name) visitor.append(&m_##name##Function);
    ${Prefix}_FOREACH_BUILTIN_FUNCTION_NAME(VISIT_FUNCTION)
#undef VISIT_FUNCTION
}

} // namespace WebCore

""").substitute(dict(Filename=filename, Prefix=prefix)))

builtinsWrapperHeader.close()

if (not os.path.exists(output_base + ".h")) or (not filecmp.cmp(output_base + ".h.tmp", output_base + ".h", shallow=False)):
    if (os.path.exists(output_base + ".h")):
        os.remove(output_base + ".h")
    os.rename(output_base + ".h.tmp", output_base + ".h")
else:
    os.remove(output_base + ".h.tmp")
